bouncycastle: Android tree with upstream code for version 1.50

Android tree as of c0d8909a6c6a4ac075a9dee7ac1fe6baff34acc0

Change-Id: I8d381554d6edec32aae8ff5bab5d5314f0954440
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509ContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509ContentVerifierProviderBuilder.java
new file mode 100644
index 0000000..af3bd09
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509ContentVerifierProviderBuilder.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.cert;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+
+public interface X509ContentVerifierProviderBuilder
+{
+    ContentVerifierProvider build(SubjectPublicKeyInfo validatingKeyInfo)
+        throws OperatorCreationException;
+
+    ContentVerifierProvider build(X509CertificateHolder validatingKeyInfo)
+        throws OperatorCreationException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/cmp/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/cmp/package.html
deleted file mode 100644
index a58af18..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/cmp/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-Basic support package for handling and creating CMP (RFC 4210) certificate management messages.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java
index 7ccfd1f..2763083 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/cmp/test/AllTests.java
@@ -36,6 +36,7 @@
 import org.bouncycastle.cert.cmp.GeneralPKIMessage;
 import org.bouncycastle.cert.cmp.ProtectedPKIMessage;
 import org.bouncycastle.cert.cmp.ProtectedPKIMessageBuilder;
+import org.bouncycastle.cert.crmf.CertificateRequestMessage;
 import org.bouncycastle.cert.crmf.CertificateRequestMessageBuilder;
 import org.bouncycastle.cert.crmf.PKMACBuilder;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessageBuilder;
@@ -224,6 +225,50 @@
         assertEquals(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, reqMsg.getPopo().getType());
     }
 
+    public void testNotBeforeNotAfter()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+
+        doNotBeforeNotAfterTest(kp, new Date(0L), new Date(60000L));
+        doNotBeforeNotAfterTest(kp, null, new Date(60000L));
+        doNotBeforeNotAfterTest(kp, new Date(0L), null);
+    }
+
+    private void doNotBeforeNotAfterTest(KeyPair kp, Date notBefore, Date notAfter)
+        throws Exception
+    {
+        CertificateRequestMessageBuilder builder = new JcaCertificateRequestMessageBuilder(
+                    BigInteger.valueOf(1)).setPublicKey(kp.getPublic()).setProofOfPossessionSubsequentMessage(
+                    SubsequentMessage.encrCert);
+
+        builder.setValidity(notBefore, notAfter);
+
+        CertificateRequestMessage message = builder.build();
+
+        if (notBefore != null)
+        {
+            assertEquals(notBefore.getTime(), message.getCertTemplate().getValidity().getNotBefore().getDate().getTime());
+        }
+        else
+        {
+            assertNull(message.getCertTemplate().getValidity().getNotBefore());
+        }
+
+        if (notAfter != null)
+        {
+            assertEquals(notAfter.getTime(), message.getCertTemplate().getValidity().getNotAfter().getDate().getTime());
+        }
+        else
+        {
+            assertNull(message.getCertTemplate().getValidity().getNotAfter());
+        }
+    }
+
     private static X509CertificateHolder makeV3Certificate(KeyPair subKP, String _subDN, KeyPair issKP, String _issDN)
         throws GeneralSecurityException, IOException, OperatorCreationException, CertException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
index 0147ffc..aa48235 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
@@ -2,6 +2,7 @@
 
 import java.math.BigInteger;
 import java.util.ArrayList;
+import java.util.Date;
 import java.util.Iterator;
 import java.util.List;
 
@@ -17,6 +18,7 @@
 import org.bouncycastle.asn1.crmf.CertRequest;
 import org.bouncycastle.asn1.crmf.CertTemplate;
 import org.bouncycastle.asn1.crmf.CertTemplateBuilder;
+import org.bouncycastle.asn1.crmf.OptionalValidity;
 import org.bouncycastle.asn1.crmf.POPOPrivKey;
 import org.bouncycastle.asn1.crmf.ProofOfPossession;
 import org.bouncycastle.asn1.crmf.SubsequentMessage;
@@ -24,6 +26,7 @@
 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x509.Time;
 import org.bouncycastle.cert.CertIOException;
 import org.bouncycastle.operator.ContentSigner;
 
@@ -90,6 +93,31 @@
         return this;
     }
 
+    /**
+     * Request a validity period for the certificate. Either, but not both, of the date parameters may be null.
+     *
+     * @param notBeforeDate not before date for certificate requested.
+     * @param notAfterDate not after date for the certificate requested.
+     *
+     * @return the current builder.
+     */
+    public CertificateRequestMessageBuilder setValidity(Date notBeforeDate, Date notAfterDate)
+    {
+        templateBuilder.setValidity(new OptionalValidity(createTime(notBeforeDate), createTime(notAfterDate)));
+
+        return this;
+    }
+
+    private Time createTime(Date date)
+    {
+        if (date != null)
+        {
+            return new Time(date);
+        }
+
+        return null;
+    }
+
     public CertificateRequestMessageBuilder addExtension(
         ASN1ObjectIdentifier oid,
         boolean              critical,
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/CRMFHelper.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/CRMFHelper.java
index 30cae1e..8747bc0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/CRMFHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/CRMFHelper.java
@@ -42,6 +42,7 @@
 import org.bouncycastle.cert.crmf.CRMFException;
 import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.jcajce.JcaJceHelper;
+import org.bouncycastle.jcajce.JcaJceUtils;
 
 class CRMFHelper
 {
@@ -157,7 +158,9 @@
             throw new CRMFException("cannot create key generator: " + e.getMessage(), e);
         }
     }
-    
+
+
+
     Cipher createContentCipher(final Key sKey, final AlgorithmIdentifier encryptionAlgID)
         throws CRMFException
     {
@@ -180,7 +183,7 @@
 
                         try
                         {
-                            params.init(sParams.getEncoded(), "ASN.1");
+                            JcaJceUtils.loadParameters(params, sParams);
                         }
                         catch (IOException e)
                         {
@@ -389,7 +392,7 @@
         {
             try
             {
-                asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1"));
+                asn1Params = JcaJceUtils.extractParameters(params);
             }
             catch (IOException e)
             {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/package.html
deleted file mode 100644
index e9bc53f..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-JCA extensions to the CRMF online certificate request package.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/package.html
deleted file mode 100644
index 521fc44..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-Basic support package for handling and creating CRMF (RFC 4211) certificate request messages.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java
new file mode 100644
index 0000000..5f4c530
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ContentVerifierProviderBuilder.java
@@ -0,0 +1,50 @@
+package org.bouncycastle.cert.jcajce;
+
+import java.security.Provider;
+import java.security.cert.CertificateException;
+
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509ContentVerifierProviderBuilder;
+import org.bouncycastle.operator.ContentVerifierProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
+
+public class JcaX509ContentVerifierProviderBuilder
+    implements X509ContentVerifierProviderBuilder
+{
+    private JcaContentVerifierProviderBuilder builder = new JcaContentVerifierProviderBuilder();
+
+    public JcaX509ContentVerifierProviderBuilder setProvider(Provider provider)
+    {
+        this.builder.setProvider(provider);
+
+        return this;
+    }
+
+    public JcaX509ContentVerifierProviderBuilder setProvider(String providerName)
+    {
+        this.builder.setProvider(providerName);
+
+        return this;
+    }
+
+    public ContentVerifierProvider build(SubjectPublicKeyInfo validatingKeyInfo)
+        throws OperatorCreationException
+    {
+        return builder.build(validatingKeyInfo);
+    }
+
+    public ContentVerifierProvider build(X509CertificateHolder validatingKeyInfo)
+        throws OperatorCreationException
+    {
+        try
+        {
+            return builder.build(validatingKeyInfo);
+        }
+        catch (CertificateException e)
+        {
+            throw new OperatorCreationException("Unable to process certificate: " + e.getMessage(), e);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/package.html
deleted file mode 100644
index cc15e01..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-JCA extensions to the certificate building and processing package.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html
deleted file mode 100644
index cfe87f2..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/jcajce/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-JCA extensions to the OCSP online certificate status package.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/package.html
deleted file mode 100644
index 234cb32..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/ocsp/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-Basic support package for handling and creating OCSP (RFC 2560) online certificate status requests.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/package.html
deleted file mode 100644
index 1b2a305..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic support package for handling and creating X.509 certificates, CRLs, and attribute certificates.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
new file mode 100644
index 0000000..f91b3a8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.cert.path;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+
+public class CertPath
+{
+    private final X509CertificateHolder[] certificates;
+
+    public CertPath(X509CertificateHolder[] certificates)
+    {
+        this.certificates = copyArray(certificates);
+    }
+
+    public X509CertificateHolder[] getCertificates()
+    {
+        return copyArray(certificates);
+    }
+
+    public CertPathValidationResult validate(CertPathValidation[] ruleSet)
+    {
+        CertPathValidationContext context = new CertPathValidationContext(CertPathUtils.getCriticalExtensionsOIDs(certificates));
+
+        for (int i = 0; i != ruleSet.length; i++)
+        {
+            for (int j = certificates.length - 1; j >= 0; j--)
+            {
+                try
+                {
+                    context.setIsEndEntity(j == 0);
+                    ruleSet[i].validate(context, certificates[j]);
+                }
+                catch (CertPathValidationException e)
+                {   // TODO: introduce object to hold (i and e)
+                    return new CertPathValidationResult(context, j, i, e);
+                }
+            }
+        }
+
+        return new CertPathValidationResult(context);
+    }
+
+    public CertPathValidationResult evaluate(CertPathValidation[] ruleSet)
+    {
+        CertPathValidationContext context = new CertPathValidationContext(CertPathUtils.getCriticalExtensionsOIDs(certificates));
+
+        CertPathValidationResultBuilder builder = new CertPathValidationResultBuilder();
+
+        for (int i = 0; i != ruleSet.length; i++)
+        {
+            for (int j = certificates.length - 1; j >= 0; j--)
+            {
+                try
+                {
+                    context.setIsEndEntity(j == 0);
+                    ruleSet[i].validate(context, certificates[j]);
+                }
+                catch (CertPathValidationException e)
+                {
+                   builder.addException(e);
+                }
+            }
+        }
+
+        return builder.build();
+    }
+
+    private X509CertificateHolder[] copyArray(X509CertificateHolder[] array)
+    {
+        X509CertificateHolder[] rv = new X509CertificateHolder[array.length];
+
+        System.arraycopy(array, 0, rv, 0, rv.length);
+
+        return rv;
+    }
+
+    public int length()
+    {
+        return certificates.length;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathUtils.java
new file mode 100644
index 0000000..4811a3d
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathUtils.java
@@ -0,0 +1,21 @@
+package org.bouncycastle.cert.path;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+
+class CertPathUtils
+{
+    static Set getCriticalExtensionsOIDs(X509CertificateHolder[] certificates)
+    {
+        Set criticalExtensions = new HashSet();
+
+        for (int i = 0; i != certificates.length; i++)
+        {
+            criticalExtensions.addAll(certificates[i].getCriticalExtensionOIDs());
+        }
+
+        return criticalExtensions;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidation.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidation.java
new file mode 100644
index 0000000..2704fe6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidation.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.cert.path;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.util.Memoable;
+
+public interface CertPathValidation
+    extends Memoable
+{
+    public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+        throws CertPathValidationException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationContext.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationContext.java
new file mode 100644
index 0000000..6a4b0ec
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationContext.java
@@ -0,0 +1,61 @@
+package org.bouncycastle.cert.path;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.util.Memoable;
+
+public class CertPathValidationContext
+    implements Memoable
+{
+    private Set criticalExtensions;
+
+    private Set handledExtensions = new HashSet();
+    private boolean endEntity;
+    private int index;
+
+    public CertPathValidationContext(Set criticalExtensionsOIDs)
+    {
+        this.criticalExtensions = criticalExtensionsOIDs;
+    }
+
+    public void addHandledExtension(ASN1ObjectIdentifier extensionIdentifier)
+    {
+        this.handledExtensions.add(extensionIdentifier);
+    }
+
+    public void setIsEndEntity(boolean isEndEntity)
+    {
+        this.endEntity = isEndEntity;
+    }
+
+    public Set getUnhandledCriticalExtensionOIDs()
+    {
+        Set rv = new HashSet(criticalExtensions);
+
+        rv.removeAll(handledExtensions);
+
+        return rv;
+    }
+
+    /**
+     * Returns true if the current certificate is the end-entity certificate.
+     *
+     * @return if current cert end-entity, false otherwise.
+     */
+    public boolean isEndEntity()
+    {
+        return endEntity;
+    }
+
+    public Memoable copy()
+    {
+        return null;  //To change body of implemented methods use File | Settings | File Templates.
+    }
+
+    public void reset(Memoable other)
+    {
+        //To change body of implemented methods use File | Settings | File Templates.
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationException.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationException.java
new file mode 100644
index 0000000..958f2d0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationException.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.cert.path;
+
+public class CertPathValidationException
+    extends Exception
+{
+    private final Exception cause;
+
+    public CertPathValidationException(String msg)
+    {
+        this(msg, null);
+    }
+
+    public CertPathValidationException(String msg, Exception cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
new file mode 100644
index 0000000..facefb4
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.cert.path;
+
+import java.util.Collections;
+import java.util.Set;
+
+public class CertPathValidationResult
+{
+    private final boolean isValid;
+    private final CertPathValidationException cause;
+    private final Set unhandledCriticalExtensionOIDs;
+
+    private int[] certIndexes;
+
+    public CertPathValidationResult(CertPathValidationContext context)
+    {
+        this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
+        this.isValid = this.unhandledCriticalExtensionOIDs.isEmpty();
+        cause = null;
+    }
+
+    public CertPathValidationResult(CertPathValidationContext context, int certIndex, int ruleIndex, CertPathValidationException cause)
+    {
+        this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
+        this.isValid = false;
+        this.cause = cause;
+    }
+
+    public CertPathValidationResult(CertPathValidationContext context, int[] certIndexes, int[] ruleIndexes, CertPathValidationException[] cause)
+    {
+        // TODO
+        this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
+        this.isValid = false;
+        this.cause = cause[0];
+        this.certIndexes = certIndexes;
+    }
+
+    public boolean isValid()
+    {
+        return isValid;
+    }
+
+    public Exception getCause()
+    {
+        if (cause != null)
+        {
+            return cause;
+        }
+
+        if (!unhandledCriticalExtensionOIDs.isEmpty())
+        {
+            return new CertPathValidationException("Unhandled Critical Extensions");
+        }
+
+        return null;
+    }
+
+    public Set getUnhandledCriticalExtensionOIDs()
+    {
+        return unhandledCriticalExtensionOIDs;
+    }
+
+    public boolean isDetailed()
+    {
+        return this.certIndexes != null;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
new file mode 100644
index 0000000..9e81339
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.cert.path;
+
+class CertPathValidationResultBuilder
+{
+    public CertPathValidationResult build()
+    {
+        return new CertPathValidationResult(null, 0, 0, null);
+    }
+
+    public void addException(CertPathValidationException exception)
+    {
+        //To change body of created methods use File | Settings | File Templates.
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathTest.java
new file mode 100644
index 0000000..3b8e23e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathTest.java
@@ -0,0 +1,369 @@
+package org.bouncycastle.cert.path.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PublicKey;
+import java.security.Security;
+import java.security.SignatureException;
+import java.security.cert.CertPath;
+import java.security.cert.CertPathBuilder;
+import java.security.cert.CertPathBuilderException;
+import java.security.cert.CertPathBuilderResult;
+import java.security.cert.CertStore;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.cert.CollectionCertStoreParameters;
+import java.security.cert.PKIXBuilderParameters;
+import java.security.cert.TrustAnchor;
+import java.security.cert.X509CertSelector;
+import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CertPathTest
+    extends SimpleTest
+{
+    public static byte[] rootCertBin = Base64.decode(
+        "MIIBqzCCARQCAQEwDQYJKoZIhvcNAQEFBQAwHjEcMBoGA1UEAxMTVGVzdCBDQSBDZXJ0aWZpY2F0ZTAeFw0wODA5MDQwNDQ1MDhaFw0wODA5MTEwNDQ1MDhaMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMRLUjhPe4YUdLo6EcjKcWUOG7CydFTH53Pr1lWjOkbmszYDpkhCTT9LOsI+disk18nkBxSl8DAHTqV+VxtuTPt64iyi10YxyDeep+DwZG/f8cVQv97U3hA9cLurZ2CofkMLGr6JpSGCMZ9FcstcTdHB4lbErIJ54YqfF4pNOs4/AgMBAAEwDQYJKoZIhvcNAQEFBQADgYEAgyrTEFY7ALpeY59jL6xFOLpuPqoBOWrUWv6O+zy5BCU0qiX71r3BpigtxRj+DYcfLIM9FNERDoHu3TthD3nwYWUBtFX8N0QUJIdJabxqAMhLjSC744koiFpCYse5Ye3ZvEdFwDzgAQsJTp5eFGgTZPkPzcdhkFJ2p9+OWs+cb24=");
+
+
+    static byte[] interCertBin = Base64.decode(
+        "MIICSzCCAbSgAwIBAgIBATANBgkqhkiG9w0BAQUFADAeMRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlMB4XDTA4MDkwNDA0NDUwOFoXDTA4MDkxMTA0NDUwOFowKDEmMCQGA1UEAxMdVGVzdCBJbnRlcm1lZGlhdGUgQ2VydGlmaWNhdGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAISS9OOZ2wxzdWny9aVvk4Joq+dwSJ+oqvHUxX3PflZyuiLiCBUOUE4q59dGKdtNX5fIfwyK3cpV0e73Y/0fwfM3m9rOWFrCKOhfeswNTes0w/2PqPVVDDsF/nj7NApuqXwioeQlgTL251RDF4sVoxXqAU7lRkcqwZt3mwqS4KTJAgMBAAGjgY4wgYswRgYDVR0jBD8wPYAUhv8BOT27EB9JaCccJD4YASPP5XWhIqQgMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGWCAQEwHQYDVR0OBBYEFL/IwAGOkHzaQyPZegy79CwM5oTFMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgGGMA0GCSqGSIb3DQEBBQUAA4GBAE4TRgUz4sUvZyVdZxqV+XyNRnqXAeLOOqFGYv2D96tQrS+zjd0elVlT6lFrtchZdOmmX7R6/H/tjMWMcTBICZyRYrvK8cCAmDOI+EIdq5p6lj2Oq6Pbw/wruojAqNrpaR6IkwNpWtdOSSupv4IJL+YU9q2YFTh4R1j3tOkPoFGr");
+
+    static byte[] finalCertBin = Base64.decode(
+        "MIICRjCCAa+gAwIBAgIBATANBgkqhkiG9w0BAQUFADAoMSYwJAYDVQQDEx1UZXN0IEludGVybWVkaWF0ZSBDZXJ0aWZpY2F0ZTAeFw0wODA5MDQwNDQ1MDhaFw0wODA5MTEwNDQ1MDhaMB8xHTAbBgNVBAMTFFRlc3QgRW5kIENlcnRpZmljYXRlMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQChpUeo0tPYywWKiLlbWKNJBcCpSaLSlaZ+4+yer1AxI5yJIVHP6SAlBghlbD5Qne5ImnN/15cz1xwYAiul6vGKJkVPlFEe2Mr+g/J/WJPQQPsjbZ1G+vxbAwXEDA4KaQrnpjRZFq+CdKHwOjuPLYS/MYQNgdIvDVEQcTbPQ8GaiQIDAQABo4GIMIGFMEYGA1UdIwQ/MD2AFL/IwAGOkHzaQyPZegy79CwM5oTFoSKkIDAeMRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlggEBMB0GA1UdDgQWBBSVkw+VpqBf3zsLc/9GdkK9TzHPwDAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIFoDANBgkqhkiG9w0BAQUFAAOBgQBLv/0bVDjzTs/y1vN3FUiZNknEbzupIZduTuXJjqv/vBX+LDPjUfu/+iOCXOSKoRn6nlOWhwB1z6taG2usQkFG8InMkRcPREi2uVgFdhJ/1C3dAWhsdlubjdL926bftXvxnx/koDzyrePW5U96RlOQM2qLvbaky2Giz6hrc3Wl+w==");
+    public static byte[] rootCrlBin = Base64.decode(
+        "MIIBYjCBzAIBATANBgkqhkiG9w0BAQsFADAeMRwwGgYDVQQDExNUZXN0IENBIENlcnRpZmljYXRlFw0wODA5MDQwNDQ1MDhaFw0wODA5MDQwNzMxNDhaMCIwIAIBAhcNMDgwOTA0MDQ0NTA4WjAMMAoGA1UdFQQDCgEJoFYwVDBGBgNVHSMEPzA9gBSG/wE5PbsQH0loJxwkPhgBI8/ldaEipCAwHjEcMBoGA1UEAxMTVGVzdCBDQSBDZXJ0aWZpY2F0ZYIBATAKBgNVHRQEAwIBATANBgkqhkiG9w0BAQsFAAOBgQCAbaFCo0BNG4AktVf6jjBLeawP1u0ELYkOCEGvYZE0mBpQ+OvFg7subZ6r3lRIj030nUli28sPFtu5ZQMBNcpE4nS1ziF44RfT3Lp5UgHx9x17Krz781iEyV+7zU8YxYMY9wULD+DCuK294kGKIssVNbmTYXZatBNoXQN5CLIocA==");
+    static byte[] interCrlBin = Base64.decode(
+        "MIIBbDCB1gIBATANBgkqhkiG9w0BAQsFADAoMSYwJAYDVQQDEx1UZXN0IEludGVybWVkaWF0ZSBDZXJ0aWZpY2F0ZRcNMDgwOTA0MDQ0NTA4WhcNMDgwOTA0MDczMTQ4WjAiMCACAQIXDTA4MDkwNDA0NDUwOFowDDAKBgNVHRUEAwoBCaBWMFQwRgYDVR0jBD8wPYAUv8jAAY6QfNpDI9l6DLv0LAzmhMWhIqQgMB4xHDAaBgNVBAMTE1Rlc3QgQ0EgQ2VydGlmaWNhdGWCAQEwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQADgYEAEVCr5TKs5yguGgLH+dBzmSPoeSIWJFLsgWwJEit/iUDJH3dgYmaczOcGxIDtbYYHLWIHM+P2YRyQz3MEkCXEgm/cx4y7leAmux5l+xQWgmxFPz+197vaphPeCZo+B7V1CWtm518gcq4mrs9ovfgNqgyFj7KGjcBpWdJE32KMt50=");
+
+    /*
+     * certpath with a circular reference
+     */
+    static byte[] certA = Base64.decode(
+        "MIIC6jCCAlOgAwIBAgIBBTANBgkqhkiG9w0BAQUFADCBjTEPMA0GA1UEAxMGSW50"
+      + "ZXIzMQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMQswCQYDVQQIEwJaSDEX"
+      + "MBUGA1UEChMOUHJpdmFzcGhlcmUgQUcxEDAOBgNVBAsTB1Rlc3RpbmcxJDAiBgkq"
+      + "hkiG9w0BCQEWFWFybWluQHByaXZhc3BoZXJlLmNvbTAeFw0wNzA0MDIwODQ2NTda"
+      + "Fw0xNzAzMzAwODQ0MDBaMIGlMScwJQYDVQQDHh4AQQByAG0AaQBuACAASADkAGIA"
+      + "ZQByAGwAaQBuAGcxCzAJBgNVBAYTAkNIMQ8wDQYDVQQHEwZadXJpY2gxCzAJBgNV"
+      + "BAgTAlpIMRcwFQYDVQQKEw5Qcml2YXNwaGVyZSBBRzEQMA4GA1UECxMHVGVzdGlu"
+      + "ZzEkMCIGCSqGSIb3DQEJARYVYXJtaW5AcHJpdmFzcGhlcmUuY29tMIGfMA0GCSqG"
+      + "SIb3DQEBAQUAA4GNADCBiQKBgQCfHfyVs5dbxG35H/Thd29qR4NZU88taCu/OWA1"
+      + "GdACI02lXWYpmLWiDgnU0ULP+GG8OnVp1IES9fz2zcrXKQ19xZzsen/To3h5sNte"
+      + "cJpS00XMM24q/jDwy5NvkBP9YIfFKQ1E/0hFHXcqwlw+b/y/v6YGsZCU2h6QDzc4"
+      + "5m0+BwIDAQABo0AwPjAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQEAwIE8DAeBglg"
+      + "hkgBhvhCAQ0EERYPeGNhIGNlcnRpZmljYXRlMA0GCSqGSIb3DQEBBQUAA4GBAJEu"
+      + "KiSfIwsY7SfobMLrv2v/BtLhGLi4RnmjiwzBhuv5rn4rRfBpq1ppmqQMJ2pmA67v"
+      + "UWCY+mNwuyjHyivpCCyJGsZ9d5H09g2vqxzkDBMz7X9VNMZYFH8j/R3/Cfvqks31"
+      + "z0OFslJkeKLa1I0P/dfVHsRKNkLRT3Ws5LKksErQ");
+
+    static byte[] certB = Base64.decode(
+        "MIICtTCCAh6gAwIBAgIBBDANBgkqhkiG9w0BAQQFADCBjTEPMA0GA1UEAxMGSW50"
+      + "ZXIyMQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMQswCQYDVQQIEwJaSDEX"
+      + "MBUGA1UEChMOUHJpdmFzcGhlcmUgQUcxEDAOBgNVBAsTB1Rlc3RpbmcxJDAiBgkq"
+      + "hkiG9w0BCQEWFWFybWluQHByaXZhc3BoZXJlLmNvbTAeFw0wNzA0MDIwODQ2Mzha"
+      + "Fw0xNzAzMzAwODQ0MDBaMIGNMQ8wDQYDVQQDEwZJbnRlcjMxCzAJBgNVBAYTAkNI"
+      + "MQ8wDQYDVQQHEwZadXJpY2gxCzAJBgNVBAgTAlpIMRcwFQYDVQQKEw5Qcml2YXNw"
+      + "aGVyZSBBRzEQMA4GA1UECxMHVGVzdGluZzEkMCIGCSqGSIb3DQEJARYVYXJtaW5A"
+      + "cHJpdmFzcGhlcmUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCxCXIB"
+      + "QRnmVvl2h7Q+0SsRxDLnyM1dJG9jMa+UCCmHy0k/ZHs5VirSbjEJSjkQ9BGeh9SC"
+      + "7JwbMpXO7UE+gcVc2RnWUY+MA+fWIeTV4KtkYA8WPu8wVGCXbN8wwh/StOocszxb"
+      + "g+iLvGeh8CYSRqg6QN3S/02etH3o8H4e7Z0PZwIDAQABoyMwITAPBgNVHRMBAf8E"
+      + "BTADAQH/MA4GA1UdDwEB/wQEAwIB9jANBgkqhkiG9w0BAQQFAAOBgQCtWdirSsmt"
+      + "+CBBCNn6ZnbU3QqQfiiQIomjenNEHESJgaS/+PvPE5i3xWFXsunTHLW321/Km16I"
+      + "7+ZvT8Su1cqHg79NAT8QB0yke1saKSy2C0Pic4HwrNqVBWFNSxMU0hQzpx/ZXDbZ"
+      + "DqIXAp5EfyRYBy2ul+jm6Rot6aFgzuopKg==");
+
+    static byte[] certC = Base64.decode(
+        "MIICtTCCAh6gAwIBAgIBAjANBgkqhkiG9w0BAQQFADCBjTEPMA0GA1UEAxMGSW50"
+      + "ZXIxMQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMQswCQYDVQQIEwJaSDEX"
+      + "MBUGA1UEChMOUHJpdmFzcGhlcmUgQUcxEDAOBgNVBAsTB1Rlc3RpbmcxJDAiBgkq"
+      + "hkiG9w0BCQEWFWFybWluQHByaXZhc3BoZXJlLmNvbTAeFw0wNzA0MDIwODQ0Mzla"
+      + "Fw0xNzAzMzAwODQ0MDBaMIGNMQ8wDQYDVQQDEwZJbnRlcjIxCzAJBgNVBAYTAkNI"
+      + "MQ8wDQYDVQQHEwZadXJpY2gxCzAJBgNVBAgTAlpIMRcwFQYDVQQKEw5Qcml2YXNw"
+      + "aGVyZSBBRzEQMA4GA1UECxMHVGVzdGluZzEkMCIGCSqGSIb3DQEJARYVYXJtaW5A"
+      + "cHJpdmFzcGhlcmUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD0rLr6"
+      + "f2/ONeJzTb0q9M/NNX+MnAFMSqiQGVBkT76u5nOH4KLkpHXkzI82JI7GuQMzoT3a"
+      + "+RP1hO6FneO92ms2soC6xiOFb4EC69Dfhh87Nww5O35JxVF0bzmbmIAWd6P/7zGh"
+      + "nd2S4tKkaZcubps+C0j9Fgi0hipVicAOUVVoDQIDAQABoyMwITAPBgNVHRMBAf8E"
+      + "BTADAQH/MA4GA1UdDwEB/wQEAwIB9jANBgkqhkiG9w0BAQQFAAOBgQCLPvc1IMA4"
+      + "YP+PmnEldyUoRWRnvPWjBGeu0WheBP7fdcnGBf93Nmc5j68ZN+eTZ5VMuZ99YdvH"
+      + "CXGNX6oodONLU//LlFKdLl5xjLAS5X9p1RbOEGytnalqeiEpjk4+C/7rIBG1kllO"
+      + "dItmI6LlEMV09Hkpg6ZRAUmRkb8KrM4X7A==");
+
+    static byte[] certD = Base64.decode(
+        "MIICtTCCAh6gAwIBAgIBBjANBgkqhkiG9w0BAQQFADCBjTEPMA0GA1UEAxMGSW50"
+      + "ZXIzMQswCQYDVQQGEwJDSDEPMA0GA1UEBxMGWnVyaWNoMQswCQYDVQQIEwJaSDEX"
+      + "MBUGA1UEChMOUHJpdmFzcGhlcmUgQUcxEDAOBgNVBAsTB1Rlc3RpbmcxJDAiBgkq"
+      + "hkiG9w0BCQEWFWFybWluQHByaXZhc3BoZXJlLmNvbTAeFw0wNzA0MDIwODQ5NTNa"
+      + "Fw0xNzAzMzAwODQ0MDBaMIGNMQ8wDQYDVQQDEwZJbnRlcjExCzAJBgNVBAYTAkNI"
+      + "MQ8wDQYDVQQHEwZadXJpY2gxCzAJBgNVBAgTAlpIMRcwFQYDVQQKEw5Qcml2YXNw"
+      + "aGVyZSBBRzEQMA4GA1UECxMHVGVzdGluZzEkMCIGCSqGSIb3DQEJARYVYXJtaW5A"
+      + "cHJpdmFzcGhlcmUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCae3TP"
+      + "jIVKeASqvNabaiUHAMGUgFxB7L0yUsIj39azLcLtUj4S7XkDf7SMGtYV0JY1XNaQ"
+      + "sHJAsnJivDZc50oiYvqDYfgFZx5+AsN5l5X5rjRzs/OX+Jo+k1OgsIyu6+mf9Kfb"
+      + "5IdWOVB2EcOg4f9tPjLM8CIj9Pp7RbKLyqUUgwIDAQABoyMwITAPBgNVHRMBAf8E"
+      + "BTADAQH/MA4GA1UdDwEB/wQEAwIB9jANBgkqhkiG9w0BAQQFAAOBgQCgr9kUdWUT"
+      + "Lt9UcztSzR3pnHRsyvS0E/z850OKQKS5/VxLEalpFvhj+3EcZ7Y6mFxaaS2B7vXg"
+      + "2YWyqV1PRb6iF7/u9EXkpSTKGrJahwANirCa3V/HTUuPdCE2GITlnWI8h3eVA+xQ"
+      + "D4LF0PXHOkXbwmhXRSb10lW1bSGkUxE9jg==");
+
+    private void testExceptions()
+        throws Exception
+    {
+        byte[] enc = { (byte)0, (byte)2, (byte)3, (byte)4, (byte)5 };
+        MyCertPath mc = new MyCertPath(enc);
+        ByteArrayOutputStream os = new ByteArrayOutputStream();
+        ByteArrayInputStream is;
+        byte[] arr;
+
+        ObjectOutputStream oOut = new ObjectOutputStream(os);
+        oOut.writeObject(mc);
+        oOut.flush();
+        oOut.close();
+
+        try
+        {
+            CertificateFactory cFac = CertificateFactory.getInstance("X.509",
+                    "BC");
+            arr = os.toByteArray();
+            is = new ByteArrayInputStream(arr);
+            cFac.generateCertPath(is);
+        }
+        catch (CertificateException e)
+        {
+            // ignore okay
+        }
+
+        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        List certCol = new ArrayList();
+
+        certCol.add(cf.generateCertificate(new ByteArrayInputStream(certA)));
+        certCol.add(cf.generateCertificate(new ByteArrayInputStream(certB)));
+        certCol.add(cf.generateCertificate(new ByteArrayInputStream(certC)));
+        certCol.add(cf.generateCertificate(new ByteArrayInputStream(certD)));
+
+        CertPathBuilder pathBuilder = CertPathBuilder.getInstance("PKIX", "BC");
+        X509CertSelector select = new X509CertSelector();
+        select.setSubject(((X509Certificate)certCol.get(0)).getSubjectX500Principal().getEncoded());
+
+        Set trustanchors = new HashSet();
+        trustanchors.add(new TrustAnchor((X509Certificate)cf.generateCertificate(new ByteArrayInputStream(rootCertBin)), null));
+
+        CertStore certStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certCol));
+
+        PKIXBuilderParameters params = new PKIXBuilderParameters(trustanchors, select);
+        params.addCertStore(certStore);
+
+        try
+        {
+            CertPathBuilderResult result = pathBuilder.build(params);
+            CertPath path = result.getCertPath();
+            fail("found cert path in circular set");
+        }
+        catch (CertPathBuilderException e) 
+        {
+            // expected
+        }
+    }
+    
+    public void performTest()
+        throws Exception
+    {
+        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
+
+        X509Certificate rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(rootCertBin));
+        X509Certificate interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(interCertBin));
+        X509Certificate finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(finalCertBin));
+
+            //Testing CertPath generation from List
+        List list = new ArrayList();
+        list.add(interCert);
+        CertPath certPath1 = cf.generateCertPath(list);
+
+            //Testing CertPath encoding as PkiPath
+        byte[] encoded = certPath1.getEncoded("PkiPath");
+
+            //Testing CertPath generation from InputStream
+        ByteArrayInputStream inStream = new ByteArrayInputStream(encoded);
+        CertPath certPath2 = cf.generateCertPath(inStream, "PkiPath");
+
+            //Comparing both CertPathes
+        if (!certPath2.equals(certPath1))
+        {
+            fail("CertPath differ after encoding and decoding.");
+        }
+
+        encoded = certPath1.getEncoded("PKCS7");
+
+            //Testing CertPath generation from InputStream
+        inStream = new ByteArrayInputStream(encoded);
+        certPath2 = cf.generateCertPath(inStream, "PKCS7");
+
+            //Comparing both CertPathes
+        if (!certPath2.equals(certPath1))
+        {
+            fail("CertPath differ after encoding and decoding.");
+        }
+
+        encoded = certPath1.getEncoded("PEM");
+
+            //Testing CertPath generation from InputStream
+        inStream = new ByteArrayInputStream(encoded);
+        certPath2 = cf.generateCertPath(inStream, "PEM");
+
+            //Comparing both CertPathes
+        if (!certPath2.equals(certPath1))
+        {
+            fail("CertPath differ after encoding and decoding.");
+        }
+
+        //
+        // empty list test
+        //
+        list = new ArrayList();
+
+        CertPath certPath = CertificateFactory.getInstance("X.509","BC").generateCertPath(list);
+        if (certPath.getCertificates().size() != 0)
+        {
+            fail("list wrong size.");
+        }
+
+        //
+        // exception tests
+        //
+        testExceptions();
+    }
+
+    public String getName()
+    {
+        return "CertPath";
+    }
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new CertPathTest());
+    }
+
+    private static class MyCertificate extends Certificate
+    {
+        private final byte[] encoding;
+
+        public MyCertificate(String type, byte[] encoding)
+        {
+            super(type);
+            // don't copy to allow null parameter in test
+            this.encoding = encoding;
+        }
+
+        public byte[] getEncoded() throws CertificateEncodingException
+        {
+            // do copy to force NPE in test
+            return (byte[])encoding.clone();
+        }
+
+        public void verify(PublicKey key) throws CertificateException,
+                NoSuchAlgorithmException, InvalidKeyException,
+                NoSuchProviderException, SignatureException
+        {
+        }
+
+        public void verify(PublicKey key, String sigProvider)
+                throws CertificateException, NoSuchAlgorithmException,
+                InvalidKeyException, NoSuchProviderException,
+                SignatureException
+        {
+        }
+
+        public String toString()
+        {
+            return "[My test Certificate, type: " + getType() + "]";
+        }
+
+        public PublicKey getPublicKey()
+        {
+            return new PublicKey()
+            {
+                public String getAlgorithm()
+                {
+                    return "TEST";
+                }
+
+                public byte[] getEncoded()
+                {
+                    return new byte[] { (byte)1, (byte)2, (byte)3 };
+                }
+
+                public String getFormat()
+                {
+                    return "TEST_FORMAT";
+                }
+            };
+        }
+    }
+
+    private static class MyCertPath extends CertPath
+    {
+        private final Vector certificates;
+
+        private final Vector encodingNames;
+
+        private final byte[] encoding;
+
+        public MyCertPath(byte[] encoding)
+        {
+            super("MyEncoding");
+            this.encoding = encoding;
+            certificates = new Vector();
+            certificates.add(new MyCertificate("MyEncoding", encoding));
+            encodingNames = new Vector();
+            encodingNames.add("MyEncoding");
+        }
+
+        public List getCertificates()
+        {
+            return Collections.unmodifiableList(certificates);
+        }
+
+        public byte[] getEncoded() throws CertificateEncodingException
+        {
+            return (byte[])encoding.clone();
+        }
+
+        public byte[] getEncoded(String encoding)
+                throws CertificateEncodingException
+        {
+            if (getType().equals(encoding))
+            {
+                return (byte[])this.encoding.clone();
+            }
+            throw new CertificateEncodingException("Encoding not supported: "
+                    + encoding);
+        }
+
+        public Iterator getEncodings()
+        {
+            return Collections.unmodifiableCollection(encodingNames).iterator();
+        }
+    }
+}
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
new file mode 100644
index 0000000..a99cb1c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
@@ -0,0 +1,403 @@
+package org.bouncycastle.cert.path.test;
+
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509ContentVerifierProviderBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509ContentVerifierProviderBuilder;
+import org.bouncycastle.cert.path.CertPath;
+import org.bouncycastle.cert.path.CertPathValidation;
+import org.bouncycastle.cert.path.CertPathValidationResult;
+import org.bouncycastle.cert.path.validations.BasicConstraintsValidation;
+import org.bouncycastle.cert.path.validations.CRLValidation;
+import org.bouncycastle.cert.path.validations.KeyUsageValidation;
+import org.bouncycastle.cert.path.validations.ParentCertIssuedValidation;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.CollectionStore;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CertPathValidationTest
+    extends SimpleTest
+{
+    private byte[] AC_PR = Base64.decode(
+           "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFU1RDQ0F6R2dBd0lC"
+        + "QWdJQkJUQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
+        + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
+        + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
+        + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
+        + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
+        + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
+        + "bHNaV2x5WVRBZUZ3MHdNakEwTURReE9UTTVNREJhRncwd05UQTBNRFF5DQpN"
+        + "elU1TURCYU1HRXhDekFKQmdOVkJBWVRBa0pTTVJNd0VRWURWUVFLRXdwSlEx"
+        + "QXRRbkpoYzJsc01UMHdPd1lEDQpWUVFERXpSQmRYUnZjbWxrWVdSbElFTmxj"
+        + "blJwWm1sallXUnZjbUVnWkdFZ1VISmxjMmxrWlc1amFXRWdaR0VnDQpVbVZ3"
+        + "ZFdKc2FXTmhNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJD"
+        + "Z0tDQVFFQXMwc0t5NGsrDQp6b016aldyMTQxeTVYQ045UGJMZERFQXN2cjZ4"
+        + "Z0NCN1l5bEhIQ1NBYmpGR3dOQ0R5NlVxN1h0VjZ6UHdIMXpGDQpFWENlS3Jm"
+        + "UUl5YXBXSEZ4V1VKajBMblFrY1RZM1FOR1huK0JuVk9EVTZDV3M1c3NoZktH"
+        + "RXZyVlQ1Z214V1NmDQp4OFlsdDgzY1dwUE1QZzg3VDlCaHVIbHQzazh2M2Ev"
+        + "NmRPbmF2dytOYTAyZExBaDBlNzZqcCtQUS9LK0pHZlBuDQphQjVVWURrZkd0"
+        + "em5uTTNBV01tY3VJK0o0ek5OMDZaa3ZnbDFsdEo2UU1qcnZEUFlSak9ndDlT"
+        + "cklpY1NmbEo4DQptVDdHWGRRaXJnQUNXc3g1QURBSklRK253TU1vNHlyTUtx"
+        + "SlFhNFFDMHhhT0QvdkdVcG9SaDQzT0FTZFp3c3YvDQpPWFlybmVJeVAwVCs4"
+        + "UUlEQVFBQm80RzNNSUcwTUQwR0ExVWRId1EyTURRd01xQXdvQzZHTEdoMGRI"
+        + "QTZMeTloDQpZM0poYVhvdWFXTndZbkpoYzJsc0xtZHZkaTVpY2k5TVExSmhZ"
+        + "M0poYVhvdVkzSnNNQklHQTFVZElBUUxNQWt3DQpCd1lGWUV3QkFRRXdIUVlE"
+        + "VlIwT0JCWUVGREpUVFlKNE9TWVB5T09KZkVMZXhDaHppK2hiTUI4R0ExVWRJ"
+        + "d1FZDQpNQmFBRklyNjhWZUVFUk0xa0VMNlYwbFVhUTJreFBBM01BNEdBMVVk"
+        + "RHdFQi93UUVBd0lCQmpBUEJnTlZIUk1CDQpBZjhFQlRBREFRSC9NQTBHQ1Nx"
+        + "R1NJYjNEUUVCQlFVQUE0SUJBUUJRUFNoZ1lidnFjaWV2SDVVb3ZMeXhkbkYr"
+        + "DQpFcjlOeXF1SWNkMnZ3Y0N1SnpKMkQ3WDBUcWhHQ0JmUEpVVkdBVWorS0NP"
+        + "SDFCVkgva1l1OUhsVHB1MGtKWFBwDQpBQlZkb2hJUERqRHhkbjhXcFFSL0Yr"
+        + "ejFDaWtVcldIMDR4eTd1N1p6UUpLSlBuR0loY1FpOElyRm1PYkllMEc3DQpY"
+        + "WTZPTjdPRUZxY21KTFFHWWdtRzFXMklXcytQd1JwWTdENGhLVEFoVjFSNkVv"
+        + "amE1L3BPcmVDL09kZXlQWmVxDQo1SUZTOUZZZk02U0Npd2hrK3l2Q1FHbVo0"
+        + "YzE5SjM0ZjVFYkRrK1NQR2tEK25EQ0E3L3VMUWNUMlJURE14SzBaDQpuZlo2"
+        + "Nm1Sc0ZjcXRGaWdScjVFcmtKZDdoUVV6eHNOV0VrNzJEVUFIcVgvNlNjeWtt"
+        + "SkR2V0plSUpqZlcNCi0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0NCg==");
+
+    private byte[] AC_RAIZ_ICPBRASIL = Base64.decode(
+          "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tDQpNSUlFdURDQ0E2Q2dBd0lC"
+        + "QWdJQkJEQU5CZ2txaGtpRzl3MEJBUVVGQURDQnRERUxNQWtHQTFVRUJoTUNR"
+        + "bEl4DQpFekFSQmdOVkJBb1RDa2xEVUMxQ2NtRnphV3d4UFRBN0JnTlZCQXNU"
+        + "TkVsdWMzUnBkSFYwYnlCT1lXTnBiMjVoDQpiQ0JrWlNCVVpXTnViMnh2WjJs"
+        + "aElHUmhJRWx1Wm05eWJXRmpZVzhnTFNCSlZFa3hFVEFQQmdOVkJBY1RDRUp5"
+        + "DQpZWE5wYkdsaE1Rc3dDUVlEVlFRSUV3SkVSakV4TUM4R0ExVUVBeE1vUVhW"
+        + "MGIzSnBaR0ZrWlNCRFpYSjBhV1pwDQpZMkZrYjNKaElGSmhhWG9nUW5KaGMy"
+        + "bHNaV2x5WVRBZUZ3MHdNVEV4TXpBeE1qVTRNREJhRncweE1URXhNekF5DQpN"
+        + "elU1TURCYU1JRzBNUXN3Q1FZRFZRUUdFd0pDVWpFVE1CRUdBMVVFQ2hNS1NV"
+        + "TlFMVUp5WVhOcGJERTlNRHNHDQpBMVVFQ3hNMFNXNXpkR2wwZFhSdklFNWhZ"
+        + "Mmx2Ym1Gc0lHUmxJRlJsWTI1dmJHOW5hV0VnWkdFZ1NXNW1iM0p0DQpZV05o"
+        + "YnlBdElFbFVTVEVSTUE4R0ExVUVCeE1JUW5KaGMybHNhV0V4Q3pBSkJnTlZC"
+        + "QWdUQWtSR01URXdMd1lEDQpWUVFERXloQmRYUnZjbWxrWVdSbElFTmxjblJw"
+        + "Wm1sallXUnZjbUVnVW1GcGVpQkNjbUZ6YVd4bGFYSmhNSUlCDQpJakFOQmdr"
+        + "cWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBd1BNdWR3WC9odm0r"
+        + "VWgyYi9sUUFjSFZBDQppc2FtYUxrV2Rrd1A5L1MvdE9LSWdSckw2T3krWklH"
+        + "bE9VZGQ2dVl0azlNYS8zcFVwZ2NmTkFqMHZZbTVnc3lqDQpRbzllbXNjK3g2"
+        + "bTRWV3drOWlxTVpTQ0s1RVFrQXEvVXQ0bjdLdUxFMStnZGZ0d2RJZ3hmVXNQ"
+        + "dDRDeU5yWTUwDQpRVjU3S00yVVQ4eDVycm16RWpyN1RJQ0dwU1VBbDJnVnFl"
+        + "NnhhaWkrYm1ZUjFRcm1XYUJTQUc1OUxya3Jqcll0DQpiUmhGYm9VRGUxREsr"
+        + "NlQ4czVMNms4Yzhva3BiSHBhOXZlTXp0RFZDOXNQSjYwTVdYaDZhblZLbzFV"
+        + "Y0xjYlVSDQp5RWVOdlpuZVZSS0FBVTZvdXdkakR2d2xzYUt5ZEZLd2VkMFRv"
+        + "UTQ3Ym1VS2djbSt3VjNlVFJrMzZVT25Ud0lEDQpBUUFCbzRIU01JSFBNRTRH"
+        + "QTFVZElBUkhNRVV3UXdZRllFd0JBUUF3T2pBNEJnZ3JCZ0VGQlFjQ0FSWXNh"
+        + "SFIwDQpjRG92TDJGamNtRnBlaTVwWTNCaWNtRnphV3d1WjI5MkxtSnlMMFJR"
+        + "UTJGamNtRnBlaTV3WkdZd1BRWURWUjBmDQpCRFl3TkRBeW9EQ2dMb1lzYUhS"
+        + "MGNEb3ZMMkZqY21GcGVpNXBZM0JpY21GemFXd3VaMjkyTG1KeUwweERVbUZq"
+        + "DQpjbUZwZWk1amNtd3dIUVlEVlIwT0JCWUVGSXI2OFZlRUVSTTFrRUw2VjBs"
+        + "VWFRMmt4UEEzTUE4R0ExVWRFd0VCDQovd1FGTUFNQkFmOHdEZ1lEVlIwUEFR"
+        + "SC9CQVFEQWdFR01BMEdDU3FHU0liM0RRRUJCUVVBQTRJQkFRQVpBNWMxDQpV"
+        + "L2hnSWg2T2NnTEFmaUpnRldwdm1EWldxbFYzMC9iSEZwajhpQm9iSlNtNXVE"
+        + "cHQ3VGlyWWgxVXhlM2ZRYUdsDQpZakplKzl6ZCtpelBSYkJxWFBWUUEzNEVY"
+        + "Y3drNHFwV3VmMWhIcmlXZmRyeDhBY3FTcXI2Q3VRRndTcjc1Rm9zDQpTemx3"
+        + "REFEYTcwbVQ3d1pqQW1RaG5aeDJ4SjZ3ZldsVDlWUWZTLy9KWWVJYzdGdWUy"
+        + "Sk5MZDAwVU9TTU1haUsvDQp0NzllbktOSEVBMmZ1cEgzdkVpZ2Y1RWg0YlZB"
+        + "TjVWb2hyVG02TVk1M3g3WFFaWnIxTUU3YTU1bEZFblNlVDB1DQptbE9BalIy"
+        + "bUFidlNNNVg1b1NaTnJtZXRkenlUajJmbENNOENDN01MYWIwa2tkbmdSSWxV"
+        + "QkdIRjEvUzVubVBiDQpLKzlBNDZzZDMzb3FLOG44DQotLS0tLUVORCBDRVJU"
+        + "SUZJQ0FURS0tLS0tDQo=");
+
+    private byte[] schefer = Base64.decode(
+          "MIIEnDCCBAWgAwIBAgICIPAwDQYJKoZIhvcNAQEEBQAwgcAxCzAJBgNVBAYT"
+        + "AkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1MDA4IFdpZXNiYWRl"
+        + "bjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAYBgNVBAsTEVNDSFVG"
+        + "QSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBCZW51dHplciBTZXJ2"
+        + "aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
+        + "ZGUwHhcNMDQwMzMwMTEwODAzWhcNMDUwMzMwMTEwODAzWjCBnTELMAkGA1UE"
+        + "BhMCREUxCjAIBgNVBAcTASAxIzAhBgNVBAoTGlNIUyBJbmZvcm1hdGlvbnNz"
+        + "eXN0ZW1lIEFHMRwwGgYDVQQLExM2MDAvMDU5NDktNjAwLzA1OTQ5MRgwFgYD"
+        + "VQQDEw9TY2hldHRlciBTdGVmYW4xJTAjBgkqhkiG9w0BCQEWFlN0ZWZhbi5T"
+        + "Y2hldHRlckBzaHMuZGUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAJD0"
+        + "95Bi76fkAMjJNTGPDiLPHmZXNsmakngDeS0juzKMeJA+TjXFouhYh6QyE4Bl"
+        + "Nf18fT4mInlgLefwf4t6meIWbiseeTo7VQdM+YrbXERMx2uHsRcgZMsiMYHM"
+        + "kVfYMK3SMJ4nhCmZxrBkoTRed4gXzVA1AA8YjjTqMyyjvt4TAgMBAAGjggHE"
+        + "MIIBwDAJBgNVHRMEAjAAMBEGCWCGSAGG+EIBAQQEAwIEsDALBgNVHQ8EBAMC"
+        + "BNAwOQYJYIZIAYb4QgENBCwWKlplcnRpZmlrYXQgbnVyIGZ1ZXIgU0NIVUZB"
+        + "LU9ubGluZSBndWVsdGlnLjAdBgNVHQ4EFgQUXReirhBfg0Yhf6MsBWoo/nPa"
+        + "hGwwge0GA1UdIwSB5TCB4oAUf2UyCaBV9JUeG9lS1Yo6OFBUdEKhgcakgcMw"
+        + "gcAxCzAJBgNVBAYTAkRFMQ8wDQYDVQQIEwZIRVNTRU4xGDAWBgNVBAcTDzY1"
+        + "MDA4IFdpZXNiYWRlbjEaMBgGA1UEChMRU0NIVUZBIEhPTERJTkcgQUcxGjAY"
+        + "BgNVBAsTEVNDSFVGQSBIT0xESU5HIEFHMSIwIAYDVQQDExlJbnRlcm5ldCBC"
+        + "ZW51dHplciBTZXJ2aWNlMSowKAYJKoZIhvcNAQkBFht6ZXJ0aWZpa2F0QHNj"
+        + "aHVmYS1vbmxpbmUuZGWCAQAwIQYDVR0RBBowGIEWU3RlZmFuLlNjaGV0dGVy"
+        + "QHNocy5kZTAmBgNVHRIEHzAdgRt6ZXJ0aWZpa2F0QHNjaHVmYS1vbmxpbmUu"
+        + "ZGUwDQYJKoZIhvcNAQEEBQADgYEAWzZtN9XQ9uyrFXqSy3hViYwV751+XZr0"
+        + "YH5IFhIS+9ixNAu8orP3bxqTaMhpwoU7T/oSsyGGSkb3fhzclgUADbA2lrOI"
+        + "GkeB/m+FArTwRbwpqhCNTwZywOp0eDosgPjCX1t53BB/m/2EYkRiYdDGsot0"
+        + "kQPOVGSjQSQ4+/D+TM8=");
+
+    // circular dependency certificates
+    private static final byte[] circCA = Base64.decode(
+        "MIIDTzCCAjegAwIBAgIDARAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
+      + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
+      + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
+      + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
+      + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3WyWDwcM58aU"
+      + "hPX4ueI1mwETt3WdQtMfIdRiCXeBrjCkYCc7nIgCmGbnfTzXSplHRgKColWh"
+      + "q/Z+1rHYayje1gjAEU2+4/r1P2pnBmPgquDuguktCIbDtCcGZu0ylyKeHh37"
+      + "aeIKzkcmRSLRzvGf/eO3RdFksrvaPaSjqCVfGRXVDKK2uftE8rIFJE+bCqow"
+      + "6+WiaAaDDiJaSJPuu5hC1NA5jw0/BFodlCuAvl1GJ8A+TICkYWcSpKS9bkSC"
+      + "0i8xdGbSSk94shA1PdDvRdFMfFys8g4aupBXV8yqqEAUkBYmOtZSJckc3W4y"
+      + "2Gx53y7vY07Xh63mcgtJs2T82WJICwIDAQABo2AwXjAdBgNVHQ4EFgQU8c/P"
+      + "NNJaL0srd9SwHwgtvwPB/3cwDgYDVR0PAQH/BAQDAgIEMBkGA1UdIAQSMBAw"
+      + "DgYMKoF6AUcDBwgAAAABMBIGA1UdEwEB/wQIMAYBAf8CAQEwDQYJKoZIhvcN"
+      + "AQEFBQADggEBAHRjYDPJKlfUzID0YzajZpgR/i2ngJrJqYeaWCmwzBgNUPad"
+      + "uBKSGHmPVg21sfULMSnirnR+e90i/D0EVzLwQzcbjPDD/85rp9QDCeMxqqPe"
+      + "9ZCHGs2BpE/HOQMP0QfQ3/Kpk7SvOH/ZcpIf6+uE6lLBQYAGs5cxvtTGOzZk"
+      + "jCVFG+TrAnF4V5sNkn3maCWiYLmyqcnxtKEFSONy2bYqqudx/dBBlRrDbRfZ"
+      + "9XsCBdiXAHY1hFHldbfDs8rslmkXJi3fJC028HZYB6oiBX/JE7BbMk7bRnUf"
+      + "HSpP7Sjxeso2SY7Yit+hQDVAlqTDGmh6kLt/hQMpsOMry4vgBL6XHKw=");
+
+    private static final byte[] circCRLCA = Base64.decode(
+       "MIIDXDCCAkSgAwIBAgIDASAAMA0GCSqGSIb3DQEBBQUAMDkxCzAJBgNVBAYT"
+     + "AkZSMRAwDgYDVQQKEwdHSVAtQ1BTMRgwFgYDVQQLEw9HSVAtQ1BTIEFOT05Z"
+     + "TUUwHhcNMDQxMDExMDAwMDAxWhcNMTQxMjMxMjM1OTU5WjA5MQswCQYDVQQG"
+     + "EwJGUjEQMA4GA1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9O"
+     + "WU1FMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwfEcFK0g7Kfo"
+     + "o5f2IBF7VEd/AG+RVGSds0Yg+u2kNYu4k04HR/+tOdBQtJvyr4W5jrQKsC5X"
+     + "skeFWMyWaFKzAjZDWB52HWp/kiMivGcxnYDuYf5piukSC+d2+vL8YaAphDzV"
+     + "HPnxEKqoM/J66uUussDTqfcL3JC/Bc7kBwn4srrsZOsamMWTQQtEqVQxNN7A"
+     + "ROSRsdiTt3hMOKditc9/NBNmjZWxgc7Twr/SaZ8CfN5wf2wuOl23knWL0QsJ"
+     + "0lSMBSBTzTcfAke4/jIT7d4nVMp3t7dsna8rt56pFK4wpRFGuCt+1P5gi51x"
+     + "xVSdI+JoNXv6zGO4o8YVaRpC5rQeGQIDAQABo20wazAfBgNVHSMEGDAWgBTx"
+     + "z8800lovSyt31LAfCC2/A8H/dzAdBgNVHQ4EFgQUGa3SbBrJx/wa2MQwhWPl"
+     + "dwLw1+IwDgYDVR0PAQH/BAQDAgECMBkGA1UdIAQSMBAwDgYMKoF6AUcDBwgA"
+     + "AAABMA0GCSqGSIb3DQEBBQUAA4IBAQAPDpYe2WPYnXTLsXSIUREBNMLmg+/7"
+     + "4Yhq9uOm5Hb5LVkDuHoEHGfmpXXEvucx5Ehu69hw+F4YSrd9wPjOiG8G6GXi"
+     + "RcrK8nE8XDvvV+E1HpJ7NKN4fSAoSb+0gliiq3aF15bvXP8nfespdd/x1xWQ"
+     + "mpYCx/mJeuqONQv2/D/7hfRKYoDBaAkWGodenPFPVs6FxwnEuH2R+KWCUdA9"
+     + "L04v8JBeL3kZiALkU7+DCCm7A0imUAgeeArbAbfIPu6eDygm+XndZ9qi7o4O"
+     + "AntPxrqbeXFIbDrQ4GV1kpxnW+XpSGDd96SWKe715gxkkDBppR5IKYJwRb6O"
+     + "1TRQIf2F+muQ");
+
+    private static final byte[] circCRL = Base64.decode(
+        "MIIB1DCBvQIBATANBgkqhkiG9w0BAQUFADA5MQswCQYDVQQGEwJGUjEQMA4G"
+      + "A1UEChMHR0lQLUNQUzEYMBYGA1UECxMPR0lQLUNQUyBBTk9OWU1FFw0xMDAx"
+      + "MDcwMzAwMTVaFw0xMDAxMTMwMzAwMTVaMACgTjBMMB8GA1UdIwQYMBaAFBmt"
+      + "0mwaycf8GtjEMIVj5XcC8NfiMAsGA1UdFAQEAgILgzAcBgNVHRIEFTATgRFh"
+      + "Yy1naXBAZ2lwLWNwcy5mcjANBgkqhkiG9w0BAQUFAAOCAQEAtF1DdFl1MQvf"
+      + "vNkbrCPuppNYcHen4+za/ZDepKuwHsH/OpKuaDJc4LndRgd5IwzfpCHkQGzt"
+      + "shK50bakN8oaYJgthKIOIJzR+fn6NMjftfR2a27Hdk2o3eQXRHQ360qMbpSy"
+      + "qPb3WfuBhxO2/DlLChJP+OxZIHtT/rNYgE0tlIv7swYi81Gq+DafzaZ9+A5t"
+      + "I0L2Gp/NUDsp5dF6PllAGiXQzl27qkcu+r50w+u0gul3nobXgbwPcMSYuWUz"
+      + "1lhA+uDn/EUWV4RSiJciCGSS10WCkFh1/YPo++mV15KDB0m+8chscrSu/bAl"
+      + "B19LxL/pCX3qr5iLE9ss3olVImyFZg==");
+
+//    private void checkCircProcessing()
+//        throws Exception
+//    {
+//        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
+//
+//        X509Certificate caCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(circCA));
+//        X509Certificate crlCaCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(circCRLCA));
+//        X509CRL crl = (X509CRL)cf.generateCRL(new ByteArrayInputStream(circCRL));
+//
+//        List list = new ArrayList();
+//
+//        list.add(caCert);
+//        list.add(crlCaCert);
+//        list.add(crl);
+//
+//        CertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+//        CertStore store = CertStore.getInstance("Collection", ccsp);
+//
+//        Calendar validDate = Calendar.getInstance();
+//        validDate.set(2010,0,8,2,21,10);
+//
+//            //validating path
+//        List certchain = new ArrayList();
+//
+//        certchain.add(crlCaCert);
+//        CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+//
+//        Set trust = new HashSet();
+//        trust.add(new TrustAnchor(caCert, null));
+//
+//        CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
+//        //PKIXParameters param = new PKIXParameters(trust);
+//
+//        PKIXBuilderParameters param = new PKIXBuilderParameters(trust, null);
+//        X509CertSelector certSelector = new X509CertSelector();
+//        certSelector.setCertificate(crlCaCert);
+//        param.setTargetCertConstraints(certSelector);
+//        param.addCertStore(store);
+//        param.setRevocationEnabled(true);
+//        param.setDate(validDate.getTime());
+//
+//        PKIXCertPathValidatorResult result = (PKIXCertPathValidatorResult)cpv.validate(cp, param);
+//    }
+
+    public void performTest()
+        throws Exception
+    {
+            // initialise CertStore
+        X509CertificateHolder rootCert = new X509CertificateHolder(CertPathTest.rootCertBin);
+        X509CertificateHolder interCert = new X509CertificateHolder(CertPathTest.interCertBin);
+        X509CertificateHolder finalCert = new X509CertificateHolder(CertPathTest.finalCertBin);
+        X509CRLHolder rootCrl = new X509CRLHolder(CertPathTest.rootCrlBin);
+        X509CRLHolder interCrl =  new X509CRLHolder(CertPathTest.interCrlBin);
+
+        CertPath path = new CertPath(new X509CertificateHolder[] { finalCert, interCert });
+        X509ContentVerifierProviderBuilder verifier = new JcaX509ContentVerifierProviderBuilder().setProvider(BouncyCastleProvider.PROVIDER_NAME);
+
+        CertPathValidationResult result = path.validate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier), new BasicConstraintsValidation(), new KeyUsageValidation()});
+
+        if (!result.isValid())
+        {
+            fail("basic validation (1) not working");
+        }
+
+        List crlList = new ArrayList();
+
+        crlList.add(rootCrl);
+        crlList.add(interCrl);
+
+        Store crls = new CollectionStore(crlList);
+
+        result = path.validate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier), new BasicConstraintsValidation(), new KeyUsageValidation(), new CRLValidation(rootCert.getSubject(), crls)});
+
+        if (!result.isValid())
+        {
+            fail("basic validation (2) not working");
+        }
+
+        result = path.validate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier), new KeyUsageValidation(), new CRLValidation(rootCert.getSubject(), crls)});
+
+        if (result.isValid() || result.getUnhandledCriticalExtensionOIDs().size() != 1
+            || !result.getUnhandledCriticalExtensionOIDs().contains(Extension.basicConstraints))
+        {
+            fail("basic validation (3) not working");
+        }
+
+        result = path.validate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier), new CRLValidation(rootCert.getSubject(), crls)});
+
+        if (result.isValid() || result.getUnhandledCriticalExtensionOIDs().size() != 2
+            || !result.getUnhandledCriticalExtensionOIDs().contains(Extension.basicConstraints)
+            || !result.getUnhandledCriticalExtensionOIDs().contains(Extension.keyUsage))
+        {
+            fail("basic validation (4) not working");
+        }
+
+        path = new CertPath(new X509CertificateHolder[] { interCert, finalCert });
+
+        result = path.validate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier)});
+
+        if (result.isValid())
+        {
+            fail("incorrect path validated!!");
+        }
+
+
+
+//        List list = new ArrayList();
+//        list.add(rootCert);
+//        list.add(interCert);
+//        list.add(finalCert);
+//        list.add(rootCrl);
+//        list.add(interCrl);
+//        CollectionCertStoreParameters ccsp = new CollectionCertStoreParameters(list);
+//        CertStore store = CertStore.getInstance("Collection", ccsp, "BC");
+//        Calendar validDate = Calendar.getInstance();
+//        validDate.set(2008,8,4,14,49,10);
+//            //validating path
+//        List certchain = new ArrayList();
+//        certchain.add(finalCert);
+//        certchain.add(interCert);
+//        CertPath cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+//        Set trust = new HashSet();
+//        trust.add(new TrustAnchor(rootCert, null));
+//
+//        CertPathValidator cpv = CertPathValidator.getInstance("PKIX","BC");
+//        PKIXParameters param = new PKIXParameters(trust);
+//        param.addCertStore(store);
+//        param.setDate(validDate.getTime());
+//        MyChecker checker = new MyChecker();
+//        param.addCertPathChecker(checker);
+//
+//        PKIXCertPathValidatorResult result =
+//            (PKIXCertPathValidatorResult) cpv.validate(cp, param);
+//        PolicyNode policyTree = result.getPolicyTree();
+//        PublicKey subjectPublicKey = result.getPublicKey();
+//
+//        if (checker.getCount() != 2)
+//        {
+//            fail("checker not evaluated for each certificate");
+//        }
+//
+//        if (!subjectPublicKey.equals(finalCert.getPublicKey()))
+//        {
+//            fail("wrong public key returned");
+//        }
+//
+//        //
+//        // invalid path containing a valid one test
+//        //
+//        try
+//        {
+//                // initialise CertStore
+//            rootCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(AC_RAIZ_ICPBRASIL));
+//            interCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(AC_PR));
+//            finalCert = (X509Certificate)cf.generateCertificate(new ByteArrayInputStream(schefer));
+//
+//            list = new ArrayList();
+//            list.add(rootCert);
+//            list.add(interCert);
+//            list.add(finalCert);
+//
+//            ccsp = new CollectionCertStoreParameters(list);
+//            store = CertStore.getInstance("Collection", ccsp);
+//            validDate = Calendar.getInstance();
+//            validDate.set(2004,2,21,2,21,10);
+//
+//                //validating path
+//            certchain = new ArrayList();
+//            certchain.add(finalCert);
+//            certchain.add(interCert);
+//            cp = CertificateFactory.getInstance("X.509","BC").generateCertPath(certchain);
+//            trust = new HashSet();
+//            trust.add(new TrustAnchor(rootCert, null));
+//
+//            cpv = CertPathValidator.getInstance("PKIX","BC");
+//            param = new PKIXParameters(trust);
+//            param.addCertStore(store);
+//            param.setRevocationEnabled(false);
+//            param.setDate(validDate.getTime());
+//
+//            result =(PKIXCertPathValidatorResult) cpv.validate(cp, param);
+//            policyTree = result.getPolicyTree();
+//            subjectPublicKey = result.getPublicKey();
+//
+//            fail("Invalid path validated");
+//        }
+//        catch (Exception e)
+//        {
+//            if (!(e instanceof CertPathValidatorException
+//                && e.getMessage().startsWith("Could not validate certificate signature.")))
+//            {
+//                fail("unexpected exception", e);
+//            }
+//        }
+//
+//        checkCircProcessing();
+    }
+
+    public String getName()
+    {
+        return "CertPathValidator";
+    }
+
+    public static void main(
+        String[]    args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new CertPathValidationTest());
+    }
+}
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/BasicConstraintsValidation.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/BasicConstraintsValidation.java
new file mode 100644
index 0000000..db4f852
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/BasicConstraintsValidation.java
@@ -0,0 +1,103 @@
+package org.bouncycastle.cert.path.validations;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.path.CertPathValidation;
+import org.bouncycastle.cert.path.CertPathValidationContext;
+import org.bouncycastle.cert.path.CertPathValidationException;
+import org.bouncycastle.util.Memoable;
+
+public class BasicConstraintsValidation
+    implements CertPathValidation
+{
+    private boolean          isMandatory;
+    private BasicConstraints bc;
+    private int              maxPathLength;
+
+    public BasicConstraintsValidation()
+    {
+        this(true);
+    }
+
+    public BasicConstraintsValidation(boolean isMandatory)
+    {
+        this.isMandatory = isMandatory;
+    }
+
+    public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+        throws CertPathValidationException
+    {
+        if (maxPathLength < 0)
+        {
+            throw new CertPathValidationException("BasicConstraints path length exceeded");
+        }
+
+        context.addHandledExtension(Extension.basicConstraints);
+
+        BasicConstraints certBC = BasicConstraints.fromExtensions(certificate.getExtensions());
+
+        if (certBC != null)
+        {
+            if (bc != null)
+            {
+                if (certBC.isCA())
+                {
+                    BigInteger pathLengthConstraint = certBC.getPathLenConstraint();
+
+                    if (pathLengthConstraint != null)
+                    {
+                        int plc = pathLengthConstraint.intValue();
+
+                        if (plc < maxPathLength)
+                        {
+                            maxPathLength = plc;
+                            bc = certBC;
+                        }
+                    }
+                }
+            }
+            else
+            {
+                bc = certBC;
+                if (certBC.isCA())
+                {
+                    maxPathLength = certBC.getPathLenConstraint().intValue();
+                }
+            }
+        }
+        else
+        {
+            if (bc != null)
+            {
+                maxPathLength--;
+            }
+        }
+
+        if (isMandatory && bc == null)
+        {
+            throw new CertPathValidationException("BasicConstraints not present in path");
+        }
+    }
+
+    public Memoable copy()
+    {
+        BasicConstraintsValidation v = new BasicConstraintsValidation(isMandatory);
+
+        v.bc = this.bc;
+        v.maxPathLength = this.maxPathLength;
+
+        return v;
+    }
+
+    public void reset(Memoable other)
+    {
+        BasicConstraintsValidation v = (BasicConstraintsValidation)other;
+
+        this.isMandatory = v.isMandatory;
+        this.bc = v.bc;
+        this.maxPathLength = v.maxPathLength;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CRLValidation.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CRLValidation.java
new file mode 100644
index 0000000..c44b7c0
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CRLValidation.java
@@ -0,0 +1,78 @@
+package org.bouncycastle.cert.path.validations;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.cert.X509CRLHolder;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.path.CertPathValidation;
+import org.bouncycastle.cert.path.CertPathValidationContext;
+import org.bouncycastle.cert.path.CertPathValidationException;
+import org.bouncycastle.util.Memoable;
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+
+public class CRLValidation
+    implements CertPathValidation
+{
+    private Store crls;
+    private X500Name workingIssuerName;
+
+    public CRLValidation(X500Name trustAnchorName, Store crls)
+    {
+        this.workingIssuerName = trustAnchorName;
+        this.crls = crls;
+    }
+
+    public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+        throws CertPathValidationException
+    {
+        // TODO: add handling of delta CRLs
+        Collection matches = crls.getMatches(new Selector()
+        {
+            public boolean match(Object obj)
+            {
+                X509CRLHolder crl = (X509CRLHolder)obj;
+
+                return (crl.getIssuer().equals(workingIssuerName));
+            }
+
+            public Object clone()
+            {
+                return this;
+            }
+        });
+
+        if (matches.isEmpty())
+        {
+            throw new CertPathValidationException("CRL for " + workingIssuerName + " not found");
+        }
+
+        for (Iterator it = matches.iterator(); it.hasNext();)
+        {
+            X509CRLHolder crl = (X509CRLHolder)it.next();
+
+            // TODO: not quite right!
+            if (crl.getRevokedCertificate(certificate.getSerialNumber()) != null)
+            {
+                throw new CertPathValidationException("Certificate revoked");
+            }
+        }
+
+        this.workingIssuerName = certificate.getSubject();
+    }
+
+    public Memoable copy()
+    {
+        return new CRLValidation(workingIssuerName, crls);
+    }
+
+    public void reset(Memoable other)
+    {
+        CRLValidation v = (CRLValidation)other;
+
+        this.workingIssuerName = v.workingIssuerName;
+        this.crls = v.crls;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CertificatePoliciesValidation.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CertificatePoliciesValidation.java
new file mode 100644
index 0000000..ebaf989
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CertificatePoliciesValidation.java
@@ -0,0 +1,146 @@
+package org.bouncycastle.cert.path.validations;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.PolicyConstraints;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.path.CertPathValidation;
+import org.bouncycastle.cert.path.CertPathValidationContext;
+import org.bouncycastle.cert.path.CertPathValidationException;
+import org.bouncycastle.util.Memoable;
+
+public class CertificatePoliciesValidation
+    implements CertPathValidation
+{
+    private int              explicitPolicy;
+    private int              policyMapping;
+    private int              inhibitAnyPolicy;
+
+    CertificatePoliciesValidation(int pathLength)
+    {
+        this(pathLength, false, false, false);
+    }
+
+    CertificatePoliciesValidation(int pathLength, boolean isExplicitPolicyRequired, boolean isAnyPolicyInhibited, boolean isPolicyMappingInhibited)
+    {
+        //
+        // (d)
+        //
+
+        if (isExplicitPolicyRequired)
+        {
+            explicitPolicy = 0;
+        }
+        else
+        {
+            explicitPolicy = pathLength + 1;
+        }
+
+        //
+        // (e)
+        //
+        if (isAnyPolicyInhibited)
+        {
+            inhibitAnyPolicy = 0;
+        }
+        else
+        {
+            inhibitAnyPolicy = pathLength + 1;
+        }
+
+        //
+        // (f)
+        //
+        if (isPolicyMappingInhibited)
+        {
+            policyMapping = 0;
+        }
+        else
+        {
+            policyMapping = pathLength + 1;
+        }
+    }
+
+    public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+        throws CertPathValidationException
+    {
+        context.addHandledExtension(Extension.policyConstraints);
+        context.addHandledExtension(Extension.inhibitAnyPolicy);
+
+        if (!context.isEndEntity())
+        {
+            if (!ValidationUtils.isSelfIssued(certificate))
+            {
+                 //
+                // H (1), (2), (3)
+                //
+                explicitPolicy = countDown(explicitPolicy);
+                policyMapping = countDown(policyMapping);
+                inhibitAnyPolicy = countDown(inhibitAnyPolicy);
+
+                //
+                // I (1), (2)
+                //
+                PolicyConstraints policyConstraints = PolicyConstraints.fromExtensions(certificate.getExtensions());
+
+                if (policyConstraints != null)
+                {
+                    BigInteger requireExplicitPolicyMapping = policyConstraints.getRequireExplicitPolicyMapping();
+                    if (requireExplicitPolicyMapping != null)
+                    {
+                        if (requireExplicitPolicyMapping.intValue() < explicitPolicy)
+                        {
+                            explicitPolicy = requireExplicitPolicyMapping.intValue();
+                        }
+                    }
+
+                    BigInteger inhibitPolicyMapping = policyConstraints.getInhibitPolicyMapping();
+                    if (inhibitPolicyMapping != null)
+                    {
+                        if (inhibitPolicyMapping.intValue() < policyMapping)
+                        {
+                            policyMapping = inhibitPolicyMapping.intValue();
+                        }
+                    }
+                }
+
+                //
+                // J
+                //
+                Extension ext = certificate.getExtension(Extension.inhibitAnyPolicy);
+
+                if (ext != null)
+                {
+                    int extValue = ASN1Integer.getInstance(ext.getParsedValue()).getValue().intValue();
+
+                    if (extValue < inhibitAnyPolicy)
+                    {
+                        inhibitAnyPolicy = extValue;
+                    }
+                }
+            }
+        }
+    }
+
+    private int countDown(int policyCounter)
+    {
+        if (policyCounter != 0)
+        {
+            return policyCounter - 1;
+        }
+
+        return 0;
+    }
+
+    public Memoable copy()
+    {
+        return new CertificatePoliciesValidation(0);    // TODO:
+    }
+
+    public void reset(Memoable other)
+    {
+        CertificatePoliciesValidation v = (CertificatePoliciesValidation)other;      // TODO:
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java
new file mode 100644
index 0000000..74b622e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/CertificatePoliciesValidationBuilder.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.cert.path.validations;
+
+import org.bouncycastle.cert.path.CertPath;
+
+public class CertificatePoliciesValidationBuilder
+{
+    private boolean isExplicitPolicyRequired;
+    private boolean isAnyPolicyInhibited;
+    private boolean isPolicyMappingInhibited;
+
+    public void setAnyPolicyInhibited(boolean anyPolicyInhibited)
+    {
+        isAnyPolicyInhibited = anyPolicyInhibited;
+    }
+
+    public void setExplicitPolicyRequired(boolean explicitPolicyRequired)
+    {
+        isExplicitPolicyRequired = explicitPolicyRequired;
+    }
+
+    public void setPolicyMappingInhibited(boolean policyMappingInhibited)
+    {
+        isPolicyMappingInhibited = policyMappingInhibited;
+    }
+
+    public CertificatePoliciesValidation build(int pathLen)
+    {
+        return new CertificatePoliciesValidation(pathLen, isExplicitPolicyRequired, isAnyPolicyInhibited, isPolicyMappingInhibited);
+    }
+
+    public CertificatePoliciesValidation build(CertPath path)
+    {
+        return build(path.length());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/KeyUsageValidation.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/KeyUsageValidation.java
new file mode 100644
index 0000000..5d9adc8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/KeyUsageValidation.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.cert.path.validations;
+
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.path.CertPathValidation;
+import org.bouncycastle.cert.path.CertPathValidationContext;
+import org.bouncycastle.cert.path.CertPathValidationException;
+import org.bouncycastle.util.Memoable;
+
+public class KeyUsageValidation
+    implements CertPathValidation
+{
+    private boolean          isMandatory;
+
+    public KeyUsageValidation()
+    {
+        this(true);
+    }
+
+    public KeyUsageValidation(boolean isMandatory)
+    {
+        this.isMandatory = isMandatory;
+    }
+
+    public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+        throws CertPathValidationException
+    {
+        context.addHandledExtension(Extension.keyUsage);
+
+        if (!context.isEndEntity())
+        {
+            KeyUsage usage = KeyUsage.fromExtensions(certificate.getExtensions());
+
+            if (usage != null)
+            {
+                if (!usage.hasUsages(KeyUsage.keyCertSign))
+                {
+                    throw new CertPathValidationException("Issuer certificate KeyUsage extension does not permit key signing");
+                }
+            }
+            else
+            {
+                if (isMandatory)
+                {
+                    throw new CertPathValidationException("KeyUsage extension not present in CA certificate");
+                }
+            }
+        }
+    }
+
+    public Memoable copy()
+    {
+        return new KeyUsageValidation(isMandatory);
+    }
+
+    public void reset(Memoable other)
+    {
+        KeyUsageValidation v = (KeyUsageValidation)other;
+
+        this.isMandatory = v.isMandatory;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/ParentCertIssuedValidation.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/ParentCertIssuedValidation.java
new file mode 100644
index 0000000..a21ad1c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/ParentCertIssuedValidation.java
@@ -0,0 +1,127 @@
+package org.bouncycastle.cert.path.validations;
+
+import java.io.IOException;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Null;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.cert.CertException;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509ContentVerifierProviderBuilder;
+import org.bouncycastle.cert.path.CertPathValidation;
+import org.bouncycastle.cert.path.CertPathValidationContext;
+import org.bouncycastle.cert.path.CertPathValidationException;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Memoable;
+
+public class ParentCertIssuedValidation
+    implements CertPathValidation
+{
+    private X509ContentVerifierProviderBuilder contentVerifierProvider;
+
+    private X500Name workingIssuerName;
+    private SubjectPublicKeyInfo workingPublicKey;
+    private AlgorithmIdentifier workingAlgId;
+
+    public ParentCertIssuedValidation(X509ContentVerifierProviderBuilder contentVerifierProvider)
+    {
+        this.contentVerifierProvider = contentVerifierProvider;
+    }
+
+    public void validate(CertPathValidationContext context, X509CertificateHolder certificate)
+        throws CertPathValidationException
+    {
+        if (workingIssuerName != null)
+        {
+           if (!workingIssuerName.equals(certificate.getIssuer()))
+           {
+               throw new CertPathValidationException("Certificate issue does not match parent");
+           }
+        }
+
+        if (workingPublicKey != null)
+        {
+            try
+            {
+                SubjectPublicKeyInfo validatingKeyInfo;
+
+                if (workingPublicKey.getAlgorithm().equals(workingAlgId))
+                {
+                    validatingKeyInfo = workingPublicKey;
+                }
+                else
+                {
+                    validatingKeyInfo = new SubjectPublicKeyInfo(workingAlgId, workingPublicKey.parsePublicKey());
+                }
+
+                if (!certificate.isSignatureValid(contentVerifierProvider.build(validatingKeyInfo)))
+                {
+                    throw new CertPathValidationException("Certificate signature not for public key in parent");
+                }
+            }
+            catch (OperatorCreationException e)
+            {
+                throw new CertPathValidationException("Unable to create verifier: " + e.getMessage(), e);
+            }
+            catch (CertException e)
+            {
+                throw new CertPathValidationException("Unable to validate signature: " + e.getMessage(), e);
+            }
+            catch (IOException e)
+            {
+                throw new CertPathValidationException("Unable to build public key: " + e.getMessage(), e);
+            }
+        }
+
+        workingIssuerName = certificate.getSubject();
+        workingPublicKey = certificate.getSubjectPublicKeyInfo();
+
+        if (workingAlgId != null)
+        {
+            // check for inherited parameters
+            if (workingPublicKey.getAlgorithm().getAlgorithm().equals(workingAlgId.getAlgorithm()))
+            {
+                if (!isNull(workingPublicKey.getAlgorithm().getParameters()))
+                {
+                    workingAlgId = workingPublicKey.getAlgorithm();
+                }
+            }
+            else
+            {
+                workingAlgId = workingPublicKey.getAlgorithm();
+            }
+        }
+        else
+        {
+            workingAlgId = workingPublicKey.getAlgorithm();
+        }
+    }
+
+    private boolean isNull(ASN1Encodable obj)
+    {
+        return obj == null || obj instanceof ASN1Null;
+    }
+
+    public Memoable copy()
+    {
+        ParentCertIssuedValidation v = new ParentCertIssuedValidation(contentVerifierProvider);
+
+        v.workingAlgId = this.workingAlgId;
+        v.workingIssuerName = this.workingIssuerName;
+        v.workingPublicKey = this.workingPublicKey;
+
+        return v;
+    }
+
+    public void reset(Memoable other)
+    {
+        ParentCertIssuedValidation v = (ParentCertIssuedValidation)other;
+
+        this.contentVerifierProvider = v.contentVerifierProvider;
+        this.workingAlgId = v.workingAlgId;
+        this.workingIssuerName = v.workingIssuerName;
+        this.workingPublicKey = v.workingPublicKey;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/ValidationUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/ValidationUtils.java
new file mode 100644
index 0000000..2a58706
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/validations/ValidationUtils.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.cert.path.validations;
+
+import org.bouncycastle.cert.X509CertificateHolder;
+
+class ValidationUtils
+{
+    static boolean isSelfIssued(X509CertificateHolder cert)
+    {
+        return cert.getSubject().equals(cert.getIssuer());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/selector/package.html b/bcpkix/src/main/java/org/bouncycastle/cert/selector/package.html
deleted file mode 100644
index c5c4211..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cert/selector/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-Specialised Selector classes for certificates, CRLs, and attribute certificates.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
index 5f382e0..018dde3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
@@ -1265,7 +1265,7 @@
     private void pemTest()
         throws Exception
     {
-        CertificateFactory cf = CertificateFactory.getInstance("X.509");
+        CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
 
         X509Certificate cert = readPEMCert(cf, PEMData.CERTIFICATE_1);
         if (cert == null)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
index 6146711..c3ca869 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
@@ -11,7 +11,6 @@
 import java.security.Signature;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.security.spec.RSAPublicKeySpec;
-import java.util.Vector;
 
 import javax.security.auth.x500.X500Principal;
 
@@ -24,11 +23,11 @@
 import org.bouncycastle.asn1.x500.X500NameBuilder;
 import org.bouncycastle.asn1.x500.style.BCStyle;
 import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.Extensions;
 import org.bouncycastle.asn1.x509.KeyUsage;
-import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
+import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
 import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
 import org.bouncycastle.jce.ECNamedCurveTable;
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
@@ -50,7 +49,6 @@
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
-import org.bouncycastle.x509.extension.SubjectKeyIdentifierStructure;
 
 /**
  **/
@@ -463,28 +461,23 @@
         KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC");
         keyGen.initialize(1024, new SecureRandom());
         KeyPair pair = keyGen.generateKeyPair();
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
 
-        Vector oids = new Vector();
-        Vector values = new Vector();
-        oids.add(X509Extension.basicConstraints);
-        values.add(new X509Extension(true, new DEROctetString(new BasicConstraints(true))));
-        oids.add(X509Extension.keyUsage);
-        values.add(new X509Extension(true, new DEROctetString(
-            new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign))));
-        SubjectKeyIdentifier subjectKeyIdentifier = new SubjectKeyIdentifierStructure(pair.getPublic());
-        X509Extension ski = new X509Extension(false, new DEROctetString(subjectKeyIdentifier));
-        oids.add(X509Extension.subjectKeyIdentifier);
-        values.add(ski);
+        Extension[] ext = new Extension[] {
+            new Extension(Extension.basicConstraints, true, new DEROctetString(new BasicConstraints(true))),
+            new Extension(Extension.keyUsage, true, new DEROctetString(new KeyUsage(KeyUsage.keyCertSign | KeyUsage.cRLSign))),
+            new Extension(Extension.subjectKeyIdentifier, false, new DEROctetString(extUtils.createSubjectKeyIdentifier(pair.getPublic())))
+        };
 
         PKCS10CertificationRequest p1 = new JcaPKCS10CertificationRequestBuilder(
             new X500Name("cn=csr"),
             pair.getPublic())
-            .addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, new X509Extensions(oids, values))
+            .addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, new Extensions(ext))
             .build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(pair.getPrivate()));
         PKCS10CertificationRequest p2 = new JcaPKCS10CertificationRequestBuilder(
             new X500Name("cn=csr"),
             pair.getPublic())
-            .addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, new X509Extensions(oids, values))
+            .addAttribute(PKCSObjectIdentifiers.pkcs_9_at_extensionRequest, new Extensions(ext))
             .build(new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(pair.getPrivate()));
 
         if (!p1.equals(p2))
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedData.java
index ec5fcfb..bd9d544 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedData.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedData.java
@@ -2,9 +2,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.AlgorithmParameters;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -14,7 +11,6 @@
 import org.bouncycastle.asn1.cms.CMSAttributes;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceAlgorithmIdentifierConverter;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Arrays;
@@ -180,7 +176,7 @@
      */
     public String getMacAlgOID()
     {
-        return macAlg.getObjectId().getId();
+        return macAlg.getAlgorithm().getId();
     }
 
     /**
@@ -200,39 +196,6 @@
     }
 
     /**
-     * Return an AlgorithmParameters object giving the MAC parameters
-     * used to digest the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws org.bouncycastle.cms.CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @throws java.security.NoSuchProviderException if the provider cannot be found.
-     * @deprecated use getMacAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getMacAlgorithmParameters(
-        String  provider)
-    throws CMSException, NoSuchProviderException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(macAlg);
-    }
-
-    /**
-     * Return an AlgorithmParameters object giving the MAC parameters
-     * used to digest the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws org.bouncycastle.cms.CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @deprecated use getMacAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getMacAlgorithmParameters(
-        Provider provider)
-    throws CMSException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(macAlg);
-    }
-
-    /**
      * return a store of the intended recipients for this message
      */
     public RecipientInformationStore getRecipientInfos()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataGenerator.java
index 3c3185f..82f8294 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataGenerator.java
@@ -3,20 +3,13 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-import java.security.SecureRandom;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
-import javax.crypto.KeyGenerator;
-
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.BEROctetString;
@@ -27,7 +20,6 @@
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceCMSMacCalculatorBuilder;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.MacCalculator;
@@ -186,81 +178,4 @@
             }
         });
     }
-
-    /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     * @deprecated no longer required, use simple constructor.
-     */
-    public CMSAuthenticatedDataGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
-    /**
-     * generate an authenticated object that contains an CMS Authenticated Data
-     * object using the given provider and the passed in key generator.
-     * @deprecated
-     */
-    private CMSAuthenticatedData generate(
-        final CMSProcessable  content,
-        String          macOID,
-        KeyGenerator    keyGen,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException
-    {
-        Provider                encProvider = keyGen.getProvider();
-
-        convertOldRecipients(rand, provider);
-
-        return generate(new CMSTypedData()
-        {
-            public ASN1ObjectIdentifier getContentType()
-            {
-                return CMSObjectIdentifiers.data;
-            }
-
-            public void write(OutputStream out)
-                throws IOException, CMSException
-            {
-                content.write(out);
-            }
-
-            public Object getContent()
-            {
-                return content;
-            }
-        }, new JceCMSMacCalculatorBuilder(new ASN1ObjectIdentifier(macOID)).setProvider(encProvider).setSecureRandom(rand).build());
-    }
-
-    /**
-     * generate an authenticated object that contains an CMS Authenticated Data
-     * object using the given provider.
-     * @deprecated use addRecipientInfoGenerator method.
-     */
-    public CMSAuthenticatedData generate(
-        CMSProcessable  content,
-        String          macOID,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        return generate(content, macOID, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * generate an authenticated object that contains an CMS Authenticated Data
-     * object using the given provider
-     * @deprecated use addRecipientInfoGenerator method..
-     */
-    public CMSAuthenticatedData generate(
-        CMSProcessable  content,
-        String          encryptionOID,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException
-    {
-        KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
-
-        return generate(content, encryptionOID, keyGen, provider);
-    }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataParser.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataParser.java
index cae9988..57ea305 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataParser.java
@@ -3,9 +3,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.AlgorithmParameters;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
@@ -22,7 +19,6 @@
 import org.bouncycastle.asn1.cms.ContentInfoParser;
 import org.bouncycastle.asn1.cms.OriginatorInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceAlgorithmIdentifierConverter;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Arrays;
@@ -143,7 +139,7 @@
             //
             // read the authenticated content info
             //
-            ContentInfoParser data = authData.getEnapsulatedContentInfo();
+            ContentInfoParser data = authData.getEncapsulatedContentInfo();
             CMSReadable readable = new CMSProcessableInputStream(
                 ((ASN1OctetStringParser)data.getContent(BERTags.OCTET_STRING)).getOctetStream());
 
@@ -176,7 +172,7 @@
             //
             // read the authenticated content info
             //
-            ContentInfoParser data = authData.getEnapsulatedContentInfo();
+            ContentInfoParser data = authData.getEncapsulatedContentInfo();
             CMSReadable readable = new CMSProcessableInputStream(
                 ((ASN1OctetStringParser)data.getContent(BERTags.OCTET_STRING)).getOctetStream());
 
@@ -233,39 +229,6 @@
     }
 
     /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the message content.
-     *
-     * @param provider the name of the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws org.bouncycastle.cms.CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @throws java.security.NoSuchProviderException if the provider cannot be found.
-     * @deprecated use getMacAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getMacAlgorithmParameters(
-        String provider)
-        throws CMSException, NoSuchProviderException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(macAlg);
-    }
-
-    /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws org.bouncycastle.cms.CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @deprecated use getMacAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getMacAlgorithmParameters(
-        Provider provider)
-        throws CMSException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(macAlg);
-    }
-
-    /**
      * return a store of the intended recipients for this message
      */
     public RecipientInformationStore getRecipientInfos()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataStreamGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataStreamGenerator.java
index 3bdd450..87afd70 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataStreamGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedDataStreamGenerator.java
@@ -2,10 +2,6 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-import java.security.SecureRandom;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -24,7 +20,6 @@
 import org.bouncycastle.asn1.cms.AuthenticatedData;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceCMSMacCalculatorBuilder;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.MacCalculator;
 import org.bouncycastle.util.io.TeeOutputStream;
@@ -312,81 +307,4 @@
             cGen.close();
         }
     }
-
-
-    /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     * @deprecated no longer of any use, use basic constructor.
-     */
-    public CMSAuthenticatedDataStreamGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
-    /**
-     * generate an authenticated object that contains an CMS Authenticated Data
-     * object using the given provider.
-     * @throws java.io.IOException
-     * @deprecated use open(out, MacCalculator)
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException
-    {
-        convertOldRecipients(rand, CMSUtils.getProvider(provider));
-
-        return open(out, new JceCMSMacCalculatorBuilder(new ASN1ObjectIdentifier(encryptionOID)).setSecureRandom(rand).setProvider(provider).build());
-    }
-
-    /**
-     * @deprecated use open(out, MacCalculator)
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException, IOException
-    {
-        convertOldRecipients(rand, provider);
-
-        return open(out, new JceCMSMacCalculatorBuilder(new ASN1ObjectIdentifier(encryptionOID)).setSecureRandom(rand).setProvider(provider).build());
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated use open(out, MacCalculator)
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        int             keySize,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException
-    {
-        convertOldRecipients(rand, CMSUtils.getProvider(provider));
-
-        return open(out, new JceCMSMacCalculatorBuilder(new ASN1ObjectIdentifier(encryptionOID), keySize).setSecureRandom(rand).setProvider(provider).build());
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated use open(out, MacCalculator)
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        int             keySize,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException, IOException
-    {
-        convertOldRecipients(rand, provider);
-
-        return open(out, new JceCMSMacCalculatorBuilder(new ASN1ObjectIdentifier(encryptionOID), keySize).setSecureRandom(rand).setProvider(provider).build());
-    }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedGenerator.java
index 064f996..4022c8e 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAuthenticatedGenerator.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.cms;
 
-import java.security.SecureRandom;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -20,17 +19,6 @@
     {
     }
 
-    /**
-     * constructor allowing specific source of randomness
-     *
-     * @param rand instance of SecureRandom to use
-     */
-    public CMSAuthenticatedGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
     public void setAuthenticatedAttributeGenerator(CMSAttributeTableGenerator authGen)
     {
         this.authGen = authGen;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedData.java
index 5a02ea9..3e44908 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedData.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedData.java
@@ -2,7 +2,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.zip.InflaterInputStream;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -58,61 +57,6 @@
         }
     }
 
-    /**
-     * Return the uncompressed content.
-     *
-     * @return the uncompressed content
-     * @throws CMSException if there is an exception uncompressing the data.
-     * @deprecated use getContent(InputExpanderProvider)
-     */
-    public byte[] getContent()
-        throws CMSException
-    {
-        ContentInfo     content = comData.getEncapContentInfo();
-
-        ASN1OctetString bytes = (ASN1OctetString)content.getContent();
-
-        InflaterInputStream     zIn = new InflaterInputStream(bytes.getOctetStream());
-
-        try
-        {
-            return CMSUtils.streamToByteArray(zIn);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("exception reading compressed stream.", e);
-        }
-    }
-
-    /**
-     * Return the uncompressed content, throwing an exception if the data size
-     * is greater than the passed in limit. If the content is exceeded getCause()
-     * on the CMSException will contain a StreamOverflowException
-     *
-     * @param limit maximum number of bytes to read
-     * @return the content read
-     * @throws CMSException if there is an exception uncompressing the data.
-     * @deprecated use getContent(InputExpanderProvider)
-     */
-    public byte[] getContent(int limit)
-        throws CMSException
-    {
-        ContentInfo     content = comData.getEncapContentInfo();
-
-        ASN1OctetString bytes = (ASN1OctetString)content.getContent();
-
-        InflaterInputStream     zIn = new InflaterInputStream(bytes.getOctetStream());
-
-        try
-        {
-            return CMSUtils.streamToByteArray(zIn, limit);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("exception reading compressed stream.", e);
-        }
-    }
-
     public ASN1ObjectIdentifier getContentType()
     {
         return contentInfo.getContentType();
@@ -145,15 +89,6 @@
     }
 
     /**
-     * return the ContentInfo 
-     * @deprecated use toASN1Structure()
-     */
-    public ContentInfo getContentInfo()
-    {
-        return contentInfo;
-    }
-
-    /**
      * return the ContentInfo
      */
     public ContentInfo toASN1Structure()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataGenerator.java
index d2b497b..d50391a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataGenerator.java
@@ -3,9 +3,7 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.zip.DeflaterOutputStream;
 
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.BEROctetString;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
@@ -38,45 +36,6 @@
 
     /**
      * generate an object that contains an CMS Compressed Data
-     * @deprecated use generate(CMSTypedData, OutputCompressor)
-     */
-    public CMSCompressedData generate(
-        CMSProcessable  content,
-        String          compressionOID)
-        throws CMSException
-    {
-        AlgorithmIdentifier     comAlgId;
-        ASN1OctetString         comOcts;
-
-        try
-        {
-            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-            DeflaterOutputStream  zOut = new DeflaterOutputStream(bOut);
-
-            content.write(zOut);
-
-            zOut.close();
-
-            comAlgId = new AlgorithmIdentifier(new ASN1ObjectIdentifier(compressionOID));
-            comOcts = new BEROctetString(bOut.toByteArray());
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("exception encoding data.", e);
-        }
-
-        ContentInfo     comContent = new ContentInfo(
-                                    CMSObjectIdentifiers.data, comOcts);
-
-        ContentInfo     contentInfo = new ContentInfo(
-                                    CMSObjectIdentifiers.compressedData,
-                                    new CompressedData(comAlgId, comContent));
-
-        return new CMSCompressedData(contentInfo);
-    }
-
-    /**
-     * generate an object that contains an CMS Compressed Data
      */
     public CMSCompressedData generate(
         CMSTypedData content,
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataParser.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataParser.java
index 910b3f0..c3da87b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataParser.java
@@ -3,7 +3,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.util.zip.InflaterInputStream;
 
 import org.bouncycastle.asn1.ASN1OctetStringParser;
 import org.bouncycastle.asn1.ASN1SequenceParser;
@@ -45,27 +44,6 @@
     }
 
     /**
-     * @deprecated  use getContent(InputExpandedProvider)
-     */
-    public CMSTypedStream  getContent()
-        throws CMSException
-    {
-        try
-        {
-            CompressedDataParser  comData = new CompressedDataParser((ASN1SequenceParser)_contentInfo.getContent(BERTags.SEQUENCE));
-            ContentInfoParser     content = comData.getEncapContentInfo();
-    
-            ASN1OctetStringParser bytes = (ASN1OctetStringParser)content.getContent(BERTags.OCTET_STRING);
-    
-            return new CMSTypedStream(content.getContentType().toString(), new InflaterInputStream(bytes.getOctetStream()));
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("IOException reading compressed content.", e);
-        }
-    }
-
-    /**
      * Return a typed stream which will allow the reading of the compressed content in
      * expanded form.
      *
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataStreamGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataStreamGenerator.java
index bb917d0..8a34eb0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataStreamGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSCompressedDataStreamGenerator.java
@@ -2,12 +2,10 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.util.zip.DeflaterOutputStream;
 
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.BERSequenceGenerator;
-import org.bouncycastle.asn1.DERSequenceGenerator;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.operator.OutputCompressor;
 
@@ -51,61 +49,15 @@
     }
 
     /**
-     * @deprecated use open(OutputStream, ContentCompressor)
+     * Open a compressing output stream with the PKCS#7 content type OID of "data".
+     *
+     * @param out the stream to encode to.
+     * @param compressor the type of compressor to use.
+     * @return an output stream to write the data be compressed to.
+     * @throws IOException
      */
     public OutputStream open(
         OutputStream out,
-        String       compressionOID) 
-        throws IOException
-    {
-        return open(out, CMSObjectIdentifiers.data.getId(), compressionOID);
-    }
-
-    /**
-     * @deprecated use open(OutputStream, ASN1ObjectIdentifier, ContentCompressor)
-     */
-    public OutputStream open(
-        OutputStream  out,        
-        String        contentOID,
-        String        compressionOID) 
-        throws IOException
-    {
-        BERSequenceGenerator sGen = new BERSequenceGenerator(out);
-        
-        sGen.addObject(CMSObjectIdentifiers.compressedData);
-        
-        //
-        // Compressed Data
-        //
-        BERSequenceGenerator cGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);
-        
-        cGen.addObject(new ASN1Integer(0));
-        
-        //
-        // AlgorithmIdentifier
-        //
-        DERSequenceGenerator algGen = new DERSequenceGenerator(cGen.getRawOutputStream());
-        
-        algGen.addObject(new ASN1ObjectIdentifier(ZLIB));
-
-        algGen.close();
-        
-        //
-        // Encapsulated ContentInfo
-        //
-        BERSequenceGenerator eiGen = new BERSequenceGenerator(cGen.getRawOutputStream());
-        
-        eiGen.addObject(new ASN1ObjectIdentifier(contentOID));
-
-        OutputStream octetStream = CMSUtils.createBEROctetOutputStream(
-            eiGen.getRawOutputStream(), 0, true, _bufferSize);
-        
-        return new CmsCompressedOutputStream(
-            new DeflaterOutputStream(octetStream), sGen, cGen, eiGen);
-    }
-
-    public OutputStream open(
-        OutputStream out,
         OutputCompressor compressor)
         throws IOException
     {
@@ -115,10 +67,10 @@
     /**
      * Open a compressing output stream.
      *
-     * @param contentOID
-     * @param out
-     * @param compressor
-     * @return
+     * @param contentOID the content type OID.
+     * @param out the stream to encode to.
+     * @param compressor the type of compressor to use.
+     * @return an output stream to write the data be compressed to.
      * @throws IOException
      */
     public OutputStream open(
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedData.java
index 131faec..56b9663 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedData.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedData.java
@@ -2,9 +2,6 @@
 
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.AlgorithmParameters;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Set;
@@ -13,7 +10,6 @@
 import org.bouncycastle.asn1.cms.EncryptedContentInfo;
 import org.bouncycastle.asn1.cms.EnvelopedData;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceAlgorithmIdentifierConverter;
 
 /**
  * containing class for an CMS Enveloped Data object
@@ -170,39 +166,6 @@
     }
 
     /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @throws NoSuchProviderException if the provider cannot be found.
-     * @deprecated use getContentEncryptionAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getEncryptionAlgorithmParameters(
-        String  provider)
-    throws CMSException, NoSuchProviderException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(encAlg);
-    }
-
-    /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @deprecated use getContentEncryptionAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getEncryptionAlgorithmParameters(
-        Provider provider)
-    throws CMSException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(encAlg);
-    }
-
-    /**
      * return a store of the intended recipients for this message
      */
     public RecipientInformationStore getRecipientInfos()
@@ -212,15 +175,6 @@
 
     /**
      * return the ContentInfo
-     * @deprecated use toASN1Structure()
-     */
-    public ContentInfo getContentInfo()
-    {
-        return contentInfo;
-    }
-
-    /**
-     * return the ContentInfo
      */
     public ContentInfo toASN1Structure()
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataGenerator.java
index 135367e..0038f90 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataGenerator.java
@@ -3,17 +3,10 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-import java.security.SecureRandom;
 import java.util.HashMap;
 import java.util.Iterator;
 
-import javax.crypto.KeyGenerator;
-
 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;
@@ -25,7 +18,6 @@
 import org.bouncycastle.asn1.cms.EncryptedContentInfo;
 import org.bouncycastle.asn1.cms.EnvelopedData;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OutputEncryptor;
 
@@ -58,65 +50,6 @@
     {
     }
 
-    /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     * @deprecated use no args constructor.
-     */
-    public CMSEnvelopedDataGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider and the passed in key generator.
-     */
-    private CMSEnvelopedData generate(
-        final CMSProcessable  content,
-        String          encryptionOID,
-        int             keySize,
-        Provider        encProvider,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException
-    {
-        convertOldRecipients(rand, provider);
-
-        JceCMSContentEncryptorBuilder builder;
-
-        if (keySize != -1)
-        {
-            builder =  new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(encryptionOID), keySize);
-        }
-        else
-        {
-            builder = new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(encryptionOID));
-        }
-
-        builder.setProvider(encProvider);
-        builder.setSecureRandom(rand);
-
-        return doGenerate(new CMSTypedData()
-        {
-            public ASN1ObjectIdentifier getContentType()
-            {
-                return CMSObjectIdentifiers.data;
-            }
-
-            public void write(OutputStream out)
-                throws IOException, CMSException
-            {
-                content.write(out);
-            }
-
-            public Object getContent()
-            {
-                return content;
-            }
-        }, builder.build());
-    }
-
     private CMSEnvelopedData doGenerate(
         CMSTypedData content,
         OutputEncryptor contentEncryptor)
@@ -184,68 +117,6 @@
     /**
      * generate an enveloped object that contains an CMS Enveloped Data
      * object using the given provider.
-     * @deprecated use OutputEncryptor method.
-     */
-    public CMSEnvelopedData generate(
-        CMSProcessable  content,
-        String          encryptionOID,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        return generate(content, encryptionOID, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated use OutputEncryptor method.
-     */
-    public CMSEnvelopedData generate(
-        CMSProcessable  content,
-        String          encryptionOID,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException
-    {
-        KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
-
-        return generate(content, encryptionOID, -1, keyGen.getProvider(), provider);
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated use OutputEncryptor method.
-     */
-    public CMSEnvelopedData generate(
-        CMSProcessable  content,
-        String          encryptionOID,
-        int             keySize,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        return generate(content, encryptionOID, keySize, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated use OutputEncryptor method.
-     */
-    public CMSEnvelopedData generate(
-        CMSProcessable  content,
-        String          encryptionOID,
-        int             keySize,
-        Provider        provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
-
-        return generate(content, encryptionOID, keySize, keyGen.getProvider(), provider);
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
      *
      * @param content the content to be encrypted
      * @param contentEncryptor the symmetric key based encryptor to encrypt the content with.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataParser.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataParser.java
index 627b0ca..defd2f7 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataParser.java
@@ -3,9 +3,6 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.AlgorithmParameters;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1EncodableVector;
@@ -20,7 +17,6 @@
 import org.bouncycastle.asn1.cms.EnvelopedDataParser;
 import org.bouncycastle.asn1.cms.OriginatorInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceAlgorithmIdentifierConverter;
 
 /**
  * Parsing class for an CMS Enveloped Data object from an input stream.
@@ -148,39 +144,6 @@
     }
 
     /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @throws NoSuchProviderException if the provider cannot be found.
-     * @deprecated use getContentEncryptionAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getEncryptionAlgorithmParameters(
-        String  provider)
-    throws CMSException, NoSuchProviderException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(encAlg);
-    }
-
-    /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the message content.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @deprecated use getContentEncryptionAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getEncryptionAlgorithmParameters(
-        Provider provider)
-    throws CMSException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(encAlg);
-    }
-
-    /**
      * Return the originator information associated with this message if present.
      *
      * @return OriginatorInformation, null if not present.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataStreamGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataStreamGenerator.java
index 072a1da..92abca0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataStreamGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedDataStreamGenerator.java
@@ -2,15 +2,9 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-import java.security.SecureRandom;
 import java.util.HashMap;
 import java.util.Iterator;
 
-import javax.crypto.KeyGenerator;
-
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -23,7 +17,6 @@
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.EnvelopedData;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OutputEncryptor;
 
@@ -61,17 +54,6 @@
     }
 
     /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     * @deprecated no longer required - specify randomness via RecipientInfoGenerator or ContentEncryptor.
-     */
-    public CMSEnvelopedDataStreamGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
-    /**
      * Set the underlying string size for encapsulated data
      * 
      * @param bufferSize length of octet strings to buffer the data.
@@ -102,39 +84,6 @@
             return new ASN1Integer(0);
         }
     }
-    
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider and the passed in key generator.
-     * @throws IOException
-     * @deprecated
-     */
-    private OutputStream open(
-        OutputStream out,
-        String       encryptionOID,
-        int          keySize,
-        Provider     encProvider,
-        Provider     provider)
-        throws NoSuchAlgorithmException, CMSException, IOException
-    {
-        convertOldRecipients(rand, provider);
-
-        JceCMSContentEncryptorBuilder builder;
-
-        if (keySize != -1)
-        {
-            builder =  new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(encryptionOID), keySize);
-        }
-        else
-        {
-            builder = new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(encryptionOID));
-        }
-
-        builder.setProvider(encProvider);
-        builder.setSecureRandom(rand);
-
-        return doOpen(CMSObjectIdentifiers.data, out, builder.build());
-    }
 
     private OutputStream doOpen(
         ASN1ObjectIdentifier dataType,
@@ -267,71 +216,6 @@
 
     /**
      * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @throws IOException
-     * @deprecated
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException
-    {
-        return open(out, encryptionOID, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * @deprecated
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException, IOException
-    {
-        KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
-
-        keyGen.init(rand);
-
-        return open(out, encryptionOID, -1, keyGen.getProvider(), provider);
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        int             keySize,
-        String          provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException, IOException
-    {
-        return open(out, encryptionOID, keySize, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
-     * object using the given provider.
-     * @deprecated
-     */
-    public OutputStream open(
-        OutputStream    out,
-        String          encryptionOID,
-        int             keySize,
-        Provider        provider)
-        throws NoSuchAlgorithmException, CMSException, IOException
-    {
-        KeyGenerator keyGen = CMSEnvelopedHelper.INSTANCE.createSymmetricKeyGenerator(encryptionOID, provider);
-
-        keyGen.init(keySize, rand);
-
-        return open(out, encryptionOID, -1, keyGen.getProvider(), provider);
-    }
-
-    /**
-     * generate an enveloped object that contains an CMS Enveloped Data
      * object using the given encryptor.
      */
     public OutputStream open(
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedGenerator.java
index aeda9a1..012b440 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedGenerator.java
@@ -1,39 +1,14 @@
 package org.bouncycastle.cms;
 
-import java.io.IOException;
-import java.security.AlgorithmParameters;
-import java.security.InvalidKeyException;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.Provider;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-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 javax.crypto.SecretKey;
-
-import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.cms.KEKIdentifier;
 import org.bouncycastle.asn1.cms.OriginatorInfo;
 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.cms.jcajce.JceKEKRecipientInfoGenerator;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipientInfoGenerator;
-import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
-import org.bouncycastle.cms.jcajce.JcePasswordRecipientInfoGenerator;
 
 /**
  * General class for generating a CMS enveloped-data message.
@@ -69,7 +44,6 @@
 
     protected CMSAttributeTableGenerator unprotectedAttributeGenerator = null;
 
-    final SecureRandom rand;
     protected OriginatorInfo originatorInfo;
 
     /**
@@ -77,17 +51,6 @@
      */
     public CMSEnvelopedGenerator()
     {
-        this(new SecureRandom());
-    }
-
-    /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     */
-    public CMSEnvelopedGenerator(
-        SecureRandom rand)
-    {
-        this.rand = rand;
     }
 
     public void setUnprotectedAttributeGenerator(CMSAttributeTableGenerator unprotectedAttributeGenerator)
@@ -95,212 +58,12 @@
         this.unprotectedAttributeGenerator = unprotectedAttributeGenerator;
     }
 
-
     public void setOriginatorInfo(OriginatorInformation originatorInfo)
     {
         this.originatorInfo = originatorInfo.toASN1Structure();
     }
 
     /**
-     * add a recipient.
-     *
-     * @deprecated use the addRecipientGenerator and JceKeyTransRecipientInfoGenerator
-     * @param cert recipient's public key certificate
-     * @exception IllegalArgumentException if there is a problem with the certificate
-     */
-    public void addKeyTransRecipient(
-        X509Certificate cert)
-        throws IllegalArgumentException
-    {
-        try
-        {
-            oldRecipientInfoGenerators.add(new JceKeyTransRecipientInfoGenerator(cert));
-        }
-        catch (CertificateEncodingException e)
-        {
-            throw new IllegalArgumentException("unable to encode certificate: " + e.getMessage());
-        }
-    }
-
-    /**
-     * add a recipient
-     *
-     * @deprecated use the addRecipientGenerator and JceKeyTransRecipientInfoGenerator
-     * @param key the public key used by the recipient
-     * @param subKeyId the identifier for the recipient's public key
-     * @exception IllegalArgumentException if there is a problem with the key
-     */
-    public void addKeyTransRecipient(
-        PublicKey   key,
-        byte[]      subKeyId)
-        throws IllegalArgumentException
-    {
-        oldRecipientInfoGenerators.add(new JceKeyTransRecipientInfoGenerator(subKeyId, key));
-    }
-
-    /**
-     * add a KEK recipient.
-     *
-     * @deprecated use the addRecipientGenerator and JceKEKRecipientInfoGenerator
-     * @param key the secret key to use for wrapping
-     * @param keyIdentifier the byte string that identifies the key
-     */
-    public void addKEKRecipient(
-        SecretKey   key,
-        byte[]      keyIdentifier)
-    {
-        addKEKRecipient(key, new KEKIdentifier(keyIdentifier, null, null));
-    }
-
-    /**
-     * add a KEK recipient.
-     *
-     * @deprecated use the addRecipientGenerator and JceKEKRecipientInfoGenerator
-     * @param key the secret key to use for wrapping
-     * @param kekIdentifier a KEKIdentifier structure (identifies the key)
-     */
-    public void addKEKRecipient(
-        SecretKey       key,
-        KEKIdentifier   kekIdentifier)
-    {
-        oldRecipientInfoGenerators.add(new JceKEKRecipientInfoGenerator(kekIdentifier, key));
-    }
-
-    /**
-     * @deprecated use addRecipientGenerator and JcePasswordRecipientInfoGenerator
-     * @param pbeKey PBE key
-     * @param kekAlgorithmOid key encryption algorithm to use.
-     */
-    public void addPasswordRecipient(
-        CMSPBEKey pbeKey,
-        String    kekAlgorithmOid)
-    {
-        oldRecipientInfoGenerators.add(new JcePasswordRecipientInfoGenerator(new ASN1ObjectIdentifier(kekAlgorithmOid), pbeKey.getPassword())
-            .setSaltAndIterationCount(pbeKey.getSalt(), pbeKey.getIterationCount())
-            .setPasswordConversionScheme((pbeKey instanceof PKCS5Scheme2UTF8PBEKey) ? PasswordRecipient.PKCS5_SCHEME2_UTF8 : PasswordRecipient.PKCS5_SCHEME2));
-    }
-
-    /**
-     * Add a key agreement based recipient.
-     *
-     * @deprecated use the addRecipientGenerator and JceKeyAgreeRecipientInfoGenerator
-     * @param agreementAlgorithm key agreement algorithm to use.
-     * @param senderPrivateKey private key to initialise sender side of agreement with.
-     * @param senderPublicKey sender public key to include with message.
-     * @param recipientCert recipient's public key certificate.
-     * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
-     * @param provider provider to use for the agreement calculation.
-     * @exception NoSuchProviderException if the specified provider cannot be found
-     * @exception NoSuchAlgorithmException if the algorithm requested cannot be found
-     * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
-     */
-    public void addKeyAgreementRecipient(
-        String           agreementAlgorithm,
-        PrivateKey       senderPrivateKey,
-        PublicKey        senderPublicKey,
-        X509Certificate  recipientCert,
-        String           cekWrapAlgorithm,
-        String           provider)
-        throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException
-    {
-        addKeyAgreementRecipient(agreementAlgorithm, senderPrivateKey, senderPublicKey, recipientCert,  cekWrapAlgorithm, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * Add a key agreement based recipient.
-     *
-     * @deprecated use the addRecipientGenerator and JceKeyAgreeRecipientInfoGenerator
-     * @param agreementAlgorithm key agreement algorithm to use.
-     * @param senderPrivateKey private key to initialise sender side of agreement with.
-     * @param senderPublicKey sender public key to include with message.
-     * @param recipientCert recipient's public key certificate.
-     * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
-     * @param provider provider to use for the agreement calculation.
-     * @exception NoSuchAlgorithmException if the algorithm requested cannot be found
-     * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
-     */
-    public void addKeyAgreementRecipient(
-        String           agreementAlgorithm,
-        PrivateKey       senderPrivateKey,
-        PublicKey        senderPublicKey,
-        X509Certificate  recipientCert,
-        String           cekWrapAlgorithm,
-        Provider         provider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        List recipients = new ArrayList();
-
-        recipients.add(recipientCert);
-
-        addKeyAgreementRecipients(agreementAlgorithm, senderPrivateKey, senderPublicKey,
-            recipients, cekWrapAlgorithm, provider);
-    }
-
-    /**
-     * Add multiple key agreement based recipients (sharing a single KeyAgreeRecipientInfo structure).
-     *
-     * @deprecated use the addRecipientGenerator and JceKeyAgreeRecipientInfoGenerator
-     * @param agreementAlgorithm key agreement algorithm to use.
-     * @param senderPrivateKey private key to initialise sender side of agreement with.
-     * @param senderPublicKey sender public key to include with message.
-     * @param recipientCerts recipients' public key certificates.
-     * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
-     * @param provider provider to use for the agreement calculation.
-     * @exception NoSuchAlgorithmException if the algorithm requested cannot be found
-     * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
-     */
-    public void addKeyAgreementRecipients(
-        String           agreementAlgorithm,
-        PrivateKey       senderPrivateKey,
-        PublicKey        senderPublicKey,
-        Collection       recipientCerts,
-        String           cekWrapAlgorithm,
-        String           provider)
-        throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeyException
-    {
-        addKeyAgreementRecipients(agreementAlgorithm, senderPrivateKey, senderPublicKey, recipientCerts, cekWrapAlgorithm, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * Add multiple key agreement based recipients (sharing a single KeyAgreeRecipientInfo structure).
-     *
-     * @deprecated use the addRecipientGenerator and JceKeyAgreeRecipientInfoGenerator
-     * @param agreementAlgorithm key agreement algorithm to use.
-     * @param senderPrivateKey private key to initialise sender side of agreement with.
-     * @param senderPublicKey sender public key to include with message.
-     * @param recipientCerts recipients' public key certificates.
-     * @param cekWrapAlgorithm OID for key wrapping algorithm to use.
-     * @param provider provider to use for the agreement calculation.
-     * @exception NoSuchAlgorithmException if the algorithm requested cannot be found
-     * @exception InvalidKeyException if the keys are inappropriate for the algorithm specified
-     */
-    public void addKeyAgreementRecipients(
-        String           agreementAlgorithm,
-        PrivateKey       senderPrivateKey,
-        PublicKey        senderPublicKey,
-        Collection       recipientCerts,
-        String           cekWrapAlgorithm,
-        Provider         provider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        JceKeyAgreeRecipientInfoGenerator recipientInfoGenerator = new JceKeyAgreeRecipientInfoGenerator(new ASN1ObjectIdentifier(agreementAlgorithm), senderPrivateKey, senderPublicKey, new ASN1ObjectIdentifier(cekWrapAlgorithm)).setProvider(provider);
-
-        for (Iterator it = recipientCerts.iterator(); it.hasNext();)
-        {
-            try
-            {
-                recipientInfoGenerator.addRecipient((X509Certificate)it.next());
-            }
-            catch (CertificateEncodingException e)
-            {
-                throw new IllegalArgumentException("unable to encode certificate: " + e.getMessage());
-            }
-        }
-
-        oldRecipientInfoGenerators.add(recipientInfoGenerator);
-    }
-
-    /**
      * Add a generator to produce the recipient info required.
      * 
      * @param recipientGenerator a generator of a recipient info object.
@@ -309,82 +72,4 @@
     {
         recipientInfoGenerators.add(recipientGenerator);
     }
-
-    protected AlgorithmIdentifier getAlgorithmIdentifier(String encryptionOID, AlgorithmParameters params) throws IOException
-    {
-        ASN1Encodable asn1Params;
-        if (params != null)
-        {
-            asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1"));
-        }
-        else
-        {
-            asn1Params = DERNull.INSTANCE;
-        }
-
-        return new AlgorithmIdentifier(
-            new ASN1ObjectIdentifier(encryptionOID),
-            asn1Params);
-    }
-
-    protected void convertOldRecipients(SecureRandom rand, Provider provider)
-    {
-        for (Iterator it = oldRecipientInfoGenerators.iterator(); it.hasNext();)
-        {
-            Object recipient = it.next();
-
-            if (recipient instanceof JceKeyTransRecipientInfoGenerator)
-            {
-                JceKeyTransRecipientInfoGenerator recip = (JceKeyTransRecipientInfoGenerator)recipient;
-
-                if (provider != null)
-                {
-                    recip.setProvider(provider);
-                }
-
-                recipientInfoGenerators.add(recip);
-            }
-            else if (recipient instanceof KEKRecipientInfoGenerator)
-            {
-                JceKEKRecipientInfoGenerator recip = (JceKEKRecipientInfoGenerator)recipient;
-
-                if (provider != null)
-                {
-                    recip.setProvider(provider);
-                }
-
-                recip.setSecureRandom(rand);
-
-                recipientInfoGenerators.add(recip);
-            }
-            else if (recipient instanceof JcePasswordRecipientInfoGenerator)
-            {
-                JcePasswordRecipientInfoGenerator recip = (JcePasswordRecipientInfoGenerator)recipient;
-
-                if (provider != null)
-                {
-                    recip.setProvider(provider);
-                }
-
-                recip.setSecureRandom(rand);
-
-                recipientInfoGenerators.add(recip);
-            }
-            else if (recipient instanceof JceKeyAgreeRecipientInfoGenerator)
-            {
-                JceKeyAgreeRecipientInfoGenerator recip = (JceKeyAgreeRecipientInfoGenerator)recipient;
-
-                if (provider != null)
-                {
-                    recip.setProvider(provider);
-                }
-
-                recip.setSecureRandom(rand);
-
-                recipientInfoGenerators.add(recip);
-            }
-        }
-
-        oldRecipientInfoGenerators.clear();
-    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedHelper.java
index fcb662b..9172706 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSEnvelopedHelper.java
@@ -3,15 +3,11 @@
 import java.io.FilterInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.security.NoSuchAlgorithmException;
-import java.security.Provider;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
-import javax.crypto.KeyGenerator;
-
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.cms.KEKRecipientInfo;
@@ -55,36 +51,7 @@
         MAC_ALG_NAMES.put(CMSEnvelopedGenerator.AES256_CBC,  "AESMac");
     }
 
-    KeyGenerator createSymmetricKeyGenerator(
-        String encryptionOID,
-        Provider provider)
-        throws NoSuchAlgorithmException
-    {
-        try
-        {
-            return createKeyGenerator(encryptionOID, provider);
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            try
-            {
-                String algName = (String)BASE_CIPHER_NAMES.get(encryptionOID);
-                if (algName != null)
-                {
-                    return createKeyGenerator(algName, provider);
-                }
-            }
-            catch (NoSuchAlgorithmException ex)
-            {
-                // ignore
-            }
-            if (provider != null)
-            {
-                return createSymmetricKeyGenerator(encryptionOID, null);
-            }
-            throw e;
-        }
-    }
+
 
     int getKeySize(String oid)
     {
@@ -98,20 +65,7 @@
         return keySize.intValue();
     }
 
-    private KeyGenerator createKeyGenerator(
-        String algName,
-        Provider provider)
-        throws NoSuchAlgorithmException
-    {
-        if (provider != null)
-        {
-            return KeyGenerator.getInstance(algName, provider);
-        }
-        else
-        {
-            return KeyGenerator.getInstance(algName);
-        }
-    }
+
 
     static RecipientInformationStore buildRecipientInformationStore(
         ASN1Set recipientInfos, AlgorithmIdentifier messageAlgorithm, CMSSecureReadable secureReadable)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSPBEKey.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSPBEKey.java
deleted file mode 100644
index d37bb31..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSPBEKey.java
+++ /dev/null
@@ -1,73 +0,0 @@
-package org.bouncycastle.cms;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-import java.security.spec.InvalidParameterSpecException;
-
-import javax.crypto.interfaces.PBEKey;
-import javax.crypto.spec.PBEParameterSpec;
-
-public abstract class CMSPBEKey
-    implements PBEKey
-{
-    private char[] password;
-    private byte[] salt;
-    private int    iterationCount;
-
-    protected static PBEParameterSpec getParamSpec(AlgorithmParameters algParams)
-        throws InvalidAlgorithmParameterException
-    {
-        try
-        {
-            return (PBEParameterSpec)algParams.getParameterSpec(PBEParameterSpec.class);
-        }
-        catch (InvalidParameterSpecException e)
-        {
-            throw new InvalidAlgorithmParameterException("cannot process PBE spec: " + e.getMessage());
-        }
-    }
-
-    public CMSPBEKey(char[] password, byte[] salt, int iterationCount)
-    {
-        this.password = password;
-        this.salt = salt;
-        this.iterationCount = iterationCount;
-    }
-
-    public CMSPBEKey(char[] password, PBEParameterSpec pbeSpec)
-    {
-        this(password, pbeSpec.getSalt(), pbeSpec.getIterationCount());
-    }
-    
-    public char[] getPassword()
-    {
-        return password;
-    }
-
-    public byte[] getSalt()
-    {
-        return salt;
-    }
-
-    public int getIterationCount()
-    {
-        return iterationCount;
-    }
-
-    public String getAlgorithm()
-    {
-        return "PKCS5S2";
-    }
-
-    public String getFormat()
-    {
-        return "RAW";
-    }
-
-    public byte[] getEncoded()
-    {
-        return null;
-    }
-
-    abstract byte[] getEncoded(String algorithmOid);
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
index 7a3cb4b..0465b77 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedData.java
@@ -3,11 +3,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-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.Collection;
 import java.util.Iterator;
@@ -25,13 +20,10 @@
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.SignedData;
 import org.bouncycastle.asn1.cms.SignerInfo;
-import org.bouncycastle.cert.jcajce.JcaCertStoreBuilder;
 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.SignatureAlgorithmIdentifierFinder;
 import org.bouncycastle.util.Store;
-import org.bouncycastle.x509.NoSuchStoreException;
-import org.bouncycastle.x509.X509Store;
 
 /**
  * general class for handling a pkcs7-signature message.
@@ -69,9 +61,7 @@
     ContentInfo             contentInfo;
     CMSTypedData            signedContent;
     SignerInformationStore  signerInfoStore;
-    X509Store               attributeStore;
-    X509Store               certificateStore;
-    X509Store               crlStore;
+
     private Map             hashes;
 
     private CMSSignedData(
@@ -266,192 +256,6 @@
     }
 
     /**
-     * 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, this.getAttributeCertificates());
-        }
-
-        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, this.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, 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 and org.bouncycastle.cert.jcajce.JcaCertStoreBuilder
-     */
-    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 and org.bouncycastle.cert.jcajce.JcaCertStoreBuilder
-     */
-    public CertStore getCertificatesAndCRLs(
-        String  type,
-        Provider  provider)
-        throws NoSuchAlgorithmException, CMSException
-    {
-        try
-        {
-            JcaCertStoreBuilder certStoreBuilder = new JcaCertStoreBuilder().setType(type);
-
-            if (provider != null)
-            {
-                certStoreBuilder.setProvider(provider);
-            }
-
-            certStoreBuilder.addCertificates(this.getCertificates());
-            certStoreBuilder.addCRLs(this.getCRLs());
-
-            return certStoreBuilder.build();
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            throw e;
-        }
-        catch (Exception e)
-        {
-            throw new CMSException("exception creating CertStore: " + e.getMessage(), e);
-        }
-    }
-
-    /**
      * Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
      *
      * @return a Store of X509CertificateHolder objects.
@@ -512,15 +316,6 @@
 
     /**
      * return the ContentInfo
-     * @deprecated use toASN1Structure()
-     */
-    public ContentInfo getContentInfo()
-    {
-        return contentInfo;
-    }
-
-    /**
-     * return the ContentInfo
      */
     public ContentInfo toASN1Structure()
     {
@@ -672,77 +467,6 @@
     /**
      * 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
-     * @deprecated use method taking Store arguments.
-     */
-    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.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
index 9692e15..eea8a1a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataGenerator.java
@@ -3,13 +3,6 @@
 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;
@@ -20,16 +13,10 @@
 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.
@@ -62,80 +49,6 @@
 {
     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
      */
@@ -144,456 +57,10 @@
     }
 
     /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     * @deprecated  rand ignored in new API, use base constructor.
-     */
-    public CMSSignedDataGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here.
+     * Generate a CMS Signed Data object carrying a detached CMS signature.
      *
-     * @param key signing key to use
-     * @param cert certificate containing corresponding public key
-     * @param digestOID digest algorithm OID
-     * @deprecated use addSignerInfoGenerator
+     * @param content the content to be signed.
      */
-    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.
-     * @deprecated use setDirectSignature() on SignerInformationGenerator.
-     */
-    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
@@ -601,6 +68,13 @@
         return generate(content, false);
     }
 
+    /**
+     * Generate a CMS Signed Data object which can be carrying a detached CMS signature, or have encapsulated data, depending on the value
+     * of the encapsulated parameter.
+     *
+     * @param content the content to be signed.
+     * @param encapsulate true if the content should be encapsulated in the signature, false otherwise.
+     */
     public CMSSignedData generate(
         // FIXME Avoid accessing more than once to support CMSProcessableInputStream
         CMSTypedData content,
@@ -747,36 +221,6 @@
      * 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)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataParser.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataParser.java
index 6c80bb4..329f089 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataParser.java
@@ -4,11 +4,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-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.HashMap;
 import java.util.Iterator;
@@ -35,15 +30,11 @@
 import org.bouncycastle.asn1.cms.SignedDataParser;
 import org.bouncycastle.asn1.cms.SignerInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cert.jcajce.JcaCertStoreBuilder;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
 import org.bouncycastle.util.Store;
 import org.bouncycastle.util.io.Streams;
-import org.bouncycastle.x509.NoSuchStoreException;
-import org.bouncycastle.x509.X509Store;
 
 /**
  * Parsing class for an CMS Signed Data object from an input stream.
@@ -100,22 +91,8 @@
     private Map                     digests;
 
     private SignerInformationStore  _signerInfoStore;
-    private X509Store               _attributeStore;
     private ASN1Set                 _certSet, _crlSet;
     private boolean                 _isCertCrlParsed;
-    private X509Store               _certificateStore;
-    private X509Store               _crlStore;
-
-    /**
-     * @deprecated use method taking a DigestCalculatorProvider
-     */
-    public CMSSignedDataParser(
-        byte[]      sigBlock)
-        throws CMSException
-    {
-        this(createDefaultDigestProvider(), new ByteArrayInputStream(sigBlock));
-    }
-
 
     public CMSSignedDataParser(
         DigestCalculatorProvider digestCalculatorProvider,
@@ -125,20 +102,6 @@
         this(digestCalculatorProvider, new ByteArrayInputStream(sigBlock));
     }
 
-    /**
-     * @deprecated use method taking digest calculator provider.
-     * @param signedContent
-     * @param sigBlock
-     * @throws CMSException
-     */
-    public CMSSignedDataParser(
-        CMSTypedStream  signedContent,
-        byte[]          sigBlock)
-        throws CMSException
-    {
-        this(createDefaultDigestProvider(), signedContent, new ByteArrayInputStream(sigBlock));
-    }
-
     public CMSSignedDataParser(
         DigestCalculatorProvider digestCalculatorProvider,
         CMSTypedStream  signedContent,
@@ -148,26 +111,8 @@
         this(digestCalculatorProvider, signedContent, new ByteArrayInputStream(sigBlock));
     }
 
-    private static DigestCalculatorProvider createDefaultDigestProvider()
-        throws CMSException
-    {
-        return new BcDigestCalculatorProvider();
-    }
-
     /**
      * base constructor - with encapsulated content
-     *
-     * @deprecated use method taking a DigestCalculatorProvider
-     */
-    public CMSSignedDataParser(
-        InputStream sigData)
-        throws CMSException
-    {
-        this(createDefaultDigestProvider(), null, sigData);
-    }
-
-     /**
-     * base constructor - with encapsulated content
      */
     public CMSSignedDataParser(
         DigestCalculatorProvider digestCalculatorProvider,
@@ -180,22 +125,6 @@
     /**
      * base constructor
      *
-     * @param signedContent the content that was signed.
-     * @param sigData the signature object stream.
-     *      *
-     * @deprecated use method taking a DigestCalculatorProvider
-     */
-    public CMSSignedDataParser(
-        CMSTypedStream  signedContent,
-        InputStream     sigData) 
-        throws CMSException
-    {
-        this(createDefaultDigestProvider(), signedContent, sigData);
-    }
-
-    /**
-     * base constructor
-     *
      * @param digestCalculatorProvider for generating accumulating digests
      * @param signedContent the content that was signed.
      * @param sigData the signature object stream.
@@ -273,11 +202,6 @@
         {
             throw new CMSException("io exception: " + e.getMessage(), e);
         }
-        
-        if (digests.isEmpty())
-        {
-            throw new CMSException("no digests could be created for message.");
-        }
     }
 
     /**
@@ -339,201 +263,6 @@
     }
 
     /**
-     * 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 org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
-     * @exception CMSException if a general exception prevents creation of the X509Store
-     * @deprecated use getAttributeCertificates()
-     */
-    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 org.bouncycastle.x509.NoSuchStoreException if the store type isn't available.
-     * @exception CMSException if a general exception prevents creation of the X509Store
-     * @deprecated use getAttributeCertificates()
-     */
-    public X509Store getAttributeCertificates(
-        String type,
-        Provider provider)
-        throws NoSuchStoreException, CMSException
-    {
-        if (_attributeStore == null)
-        {
-            populateCertCrlSets();
-
-            _attributeStore = HELPER.createAttributeStore(type, provider, this.getAttributeCertificates());
-        }
-
-        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 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 getCertificates()
-     */
-    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 getCertificates()
-     */
-    public X509Store getCertificates(
-        String type,
-        Provider provider)
-        throws NoSuchStoreException, CMSException
-    {
-        if (_certificateStore == null)
-        {
-            populateCertCrlSets();
-
-            _certificateStore = HELPER.createCertificateStore(type, provider, this.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 getCRLs()
-     */
-    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 getCRLs()
-     */
-    public X509Store getCRLs(
-        String type,
-        Provider provider)
-        throws NoSuchStoreException, CMSException
-    {
-        if (_crlStore == null)
-        {
-            populateCertCrlSets();
-
-            _crlStore = HELPER.createCRLsStore(type, provider, 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 getCertificates() and org.bouncycastle.cert.jcajce.JcaCertStoreBuilder
-     */
-    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 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 getCertificates() and org.bouncycastle.cert.jcajce.JcaCertStoreBuilder
-     */
-    public CertStore getCertificatesAndCRLs(
-        String  type,
-        Provider  provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        populateCertCrlSets();
-
-        try
-        {
-            JcaCertStoreBuilder certStoreBuilder = new JcaCertStoreBuilder().setType(type);
-
-            if (provider != null)
-            {
-                certStoreBuilder.setProvider(provider);
-            }
-
-            certStoreBuilder.addCertificates(this.getCertificates());
-            certStoreBuilder.addCRLs(this.getCRLs());
-
-            return certStoreBuilder.build();
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            throw e;
-        }
-        catch (Exception e)
-        {
-            throw new CMSException("exception creating CertStore: " + e.getMessage(), e);
-        }
-    }
-
-    /**
      * Return any X.509 certificate objects in this SignedData structure as a Store of X509CertificateHolder objects.
      *
      * @return a Store of X509CertificateHolder objects.
@@ -719,102 +448,6 @@
      * The output stream is returned unclosed.
      * </p>
      * @param original the signed data stream to be used as a base.
-     * @param certsAndCrls the new certificates and CRLs to be used.
-     * @param out the stream to write the new signed data object to.
-     * @return out.
-     * @exception CMSException if there is an error processing the CertStore
-     * @deprecated use method that takes Store objects.
-     */
-    public static OutputStream replaceCertificatesAndCRLs(
-        InputStream   original,
-        CertStore     certsAndCrls,
-        OutputStream  out)
-        throws CMSException, IOException
-    {
-        ASN1StreamParser in = new ASN1StreamParser(original);
-        ContentInfoParser contentInfo = new ContentInfoParser((ASN1SequenceParser)in.readObject());
-        SignedDataParser signedData = SignedDataParser.getInstance(contentInfo.getContent(BERTags.SEQUENCE));
-
-        BERSequenceGenerator sGen = new BERSequenceGenerator(out);
-
-        sGen.addObject(CMSObjectIdentifiers.signedData);
-
-        BERSequenceGenerator sigGen = new BERSequenceGenerator(sGen.getRawOutputStream(), 0, true);
-
-        // version number
-        sigGen.addObject(signedData.getVersion());
-
-        // digests
-        sigGen.getRawOutputStream().write(signedData.getDigestAlgorithms().toASN1Primitive().getEncoded());
-
-        // encap content info
-        ContentInfoParser encapContentInfo = signedData.getEncapContentInfo();
-
-        BERSequenceGenerator eiGen = new BERSequenceGenerator(sigGen.getRawOutputStream());
-
-        eiGen.addObject(encapContentInfo.getContentType());
-
-        pipeEncapsulatedOctetString(encapContentInfo, eiGen.getRawOutputStream());
-
-        eiGen.close();
-
-        //
-        // skip existing certs and CRLs
-        //
-        getASN1Set(signedData.getCertificates());
-        getASN1Set(signedData.getCrls());
-
-        //
-        // replace the certs and crls in the SignedData object
-        //
-        ASN1Set certs;
-
-        try
-        {
-            certs = CMSUtils.createBerSetFromList(CMSUtils.getCertificatesFromStore(certsAndCrls));
-        }
-        catch (CertStoreException e)
-        {
-            throw new CMSException("error getting certs from certStore", e);
-        }
-
-        if (certs.size() > 0)
-        {
-            sigGen.getRawOutputStream().write(new DERTaggedObject(false, 0, certs).getEncoded());
-        }
-
-        ASN1Set crls;
-
-        try
-        {
-            crls = CMSUtils.createBerSetFromList(CMSUtils.getCRLsFromStore(certsAndCrls));
-        }
-        catch (CertStoreException e)
-        {
-            throw new CMSException("error getting crls from certStore", e);
-        }
-
-        if (crls.size() > 0)
-        {
-            sigGen.getRawOutputStream().write(new DERTaggedObject(false, 1, crls).getEncoded());
-        }
-
-        sigGen.getRawOutputStream().write(signedData.getSignerInfos().toASN1Primitive().getEncoded());
-
-        sigGen.close();
-
-        sGen.close();
-
-        return out;
-    }
-
-    /**
-     * Replace the certificate and CRL information associated with this
-     * CMSSignedData object with the new one passed in.
-     * <p>
-     * The output stream is returned unclosed.
-     * </p>
-     * @param original the signed data stream to be used as a base.
      * @param certs new certificates to be used, if any.
      * @param crls new CRLs to be used, if any.
      * @param attrCerts new attribute certificates to be used, if any.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
index cbd1c50..1e09b48 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
@@ -2,14 +2,6 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.InvalidKeyException;
-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.Iterator;
 import java.util.List;
 
@@ -21,14 +13,8 @@
 import org.bouncycastle.asn1.BERSequenceGenerator;
 import org.bouncycastle.asn1.BERTaggedObject;
 import org.bouncycastle.asn1.DERSet;
-import org.bouncycastle.asn1.cms.AttributeTable;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 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.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 
 /**
  * General class for generating a pkcs7-signature message stream.
@@ -71,17 +57,6 @@
     }
 
     /**
-     * constructor allowing specific source of randomness
-     * @param rand instance of SecureRandom to use
-     * @deprecated no longer required if the addSignerInfoGenerator method is used.
-     */
-    public CMSSignedDataStreamGenerator(
-        SecureRandom rand)
-    {
-        super(rand);
-    }
-
-    /**
      * Set the underlying string size for encapsulated data
      * 
      * @param bufferSize length of octet strings to buffer the data.
@@ -91,514 +66,6 @@
     {
         _bufferSize = bufferSize;
     }
-    
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchProviderException 
-     * @throws NoSuchAlgorithmException 
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          digestOID,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, cert, digestOID, CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          digestOID,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-       addSigner(key, cert, digestOID, new DefaultSignedAttributeTableGenerator(),
-           (CMSAttributeTableGenerator)null, sigProvider);
-    }
-
-    /**
-     * add a signer, specifying the digest encryption algorithm - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchProviderException
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          encryptionOID,
-        String          digestOID,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, cert, encryptionOID, digestOID, CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * add a signer, specifying digest encryptionOID - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          encryptionOID,
-        String          digestOID,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-       addSigner(key, cert, encryptionOID, digestOID, new DefaultSignedAttributeTableGenerator(),
-           (CMSAttributeTableGenerator)null, sigProvider);
-    }
-
-    /**
-     * add a signer with extra signed/unsigned attributes.
-     * @throws NoSuchProviderException 
-     * @throws NoSuchAlgorithmException 
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          digestOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, cert, digestOID, signedAttr, unsignedAttr,
-            CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * add a signer with extra signed/unsigned attributes.
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          digestOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, cert, digestOID, new DefaultSignedAttributeTableGenerator(signedAttr),
-            new SimpleAttributeTableGenerator(unsignedAttr), sigProvider);
-    }
-
-    /**
-     * add a signer with extra signed/unsigned attributes - specifying digest
-     * encryption algorithm.
-     * @throws NoSuchProviderException
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          encryptionOID,
-        String          digestOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, cert, encryptionOID, digestOID, signedAttr, unsignedAttr,
-            CMSUtils.getProvider(sigProvider));
-    }
-
-   /**
-     * add a signer with extra signed/unsigned attributes and the digest encryption algorithm.
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          encryptionOID,
-        String          digestOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, cert, encryptionOID, digestOID,
-            new DefaultSignedAttributeTableGenerator(signedAttr),
-            new SimpleAttributeTableGenerator(unsignedAttr), sigProvider);
-    }
-
-    /**
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        X509Certificate             cert,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        String                      sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, cert, digestOID, signedAttrGenerator, unsignedAttrGenerator,
-            CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        X509Certificate             cert,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        Provider                    sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, cert, getEncOID(key, digestOID), digestOID, signedAttrGenerator,
-            unsignedAttrGenerator, sigProvider);
-    }
-
-    /**
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        X509Certificate             cert,
-        String                      encryptionOID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        String                      sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, cert, encryptionOID, digestOID, signedAttrGenerator, unsignedAttrGenerator,
-            CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        X509Certificate             cert,
-        String                      encryptionOID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        Provider                    sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, cert, encryptionOID, digestOID, signedAttrGenerator, unsignedAttrGenerator, sigProvider, sigProvider);
-    }
-
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchProviderException
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        byte[]          subjectKeyID,
-        String          digestOID,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, digestOID, CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        byte[]          subjectKeyID,
-        String          digestOID,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-       addSigner(key, subjectKeyID, digestOID, new DefaultSignedAttributeTableGenerator(),
-           (CMSAttributeTableGenerator)null, sigProvider);
-    }
-
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here.
-     * @throws NoSuchProviderException
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignedInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        byte[]          subjectKeyID,
-        String          encryptionOID,
-        String          digestOID,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, encryptionOID, digestOID, CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * add a signer - no attributes other than the default ones will be
-     * provided here, specifying the digest encryption algorithm.
-     *
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        byte[]          subjectKeyID,
-        String          encryptionOID,
-        String          digestOID,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-       addSigner(key, subjectKeyID, encryptionOID, digestOID,
-           new DefaultSignedAttributeTableGenerator(), (CMSAttributeTableGenerator)null,
-           sigProvider);
-    }
-
-    /**
-     * add a signer with extra signed/unsigned attributes.
-     * @throws NoSuchProviderException
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        byte[]          subjectKeyID,
-        String          digestOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr,
-        String          sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, digestOID, signedAttr, unsignedAttr,
-            CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * add a signer with extra signed/unsigned attributes.
-     * @throws NoSuchAlgorithmException
-     * @throws InvalidKeyException
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey      key,
-        byte[]          subjectKeyID,
-        String          digestOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr,
-        Provider        sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, digestOID,
-            new DefaultSignedAttributeTableGenerator(signedAttr),
-            new SimpleAttributeTableGenerator(unsignedAttr), sigProvider);
-    }
-
-    /**
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        byte[]                      subjectKeyID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        String                      sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, digestOID, signedAttrGenerator, unsignedAttrGenerator,
-            CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        byte[]                      subjectKeyID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        Provider                    sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, getEncOID(key, digestOID), digestOID, signedAttrGenerator,
-            unsignedAttrGenerator, sigProvider);
-    }
-
-    /**
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        byte[]                      subjectKeyID,
-        String                      encryptionOID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        String                      sigProvider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, encryptionOID, digestOID, signedAttrGenerator,
-            unsignedAttrGenerator, CMSUtils.getProvider(sigProvider));
-    }
-
-    /**
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        byte[]                      subjectKeyID,
-        String                      encryptionOID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        Provider                    sigProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        addSigner(key, subjectKeyID, encryptionOID, digestOID, signedAttrGenerator, unsignedAttrGenerator, sigProvider, sigProvider);
-    }
-
-    /**
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        X509Certificate             cert,
-        String                      encryptionOID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        Provider                    sigProvider,
-        Provider                    digProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        doAddSigner(key, cert, encryptionOID, digestOID, signedAttrGenerator, unsignedAttrGenerator, sigProvider, digProvider);
-    }
-
-    private void doAddSigner(PrivateKey key, Object signerId, String encryptionOID, String digestOID, CMSAttributeTableGenerator signedAttrGenerator, CMSAttributeTableGenerator unsignedAttrGenerator, Provider sigProvider, Provider digProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        String          digestName = CMSSignedHelper.INSTANCE.getDigestAlgName(digestOID);
-        String          signatureName = digestName + "with" + CMSSignedHelper.INSTANCE.getEncryptionAlgName(encryptionOID);
-
-        JcaContentSignerBuilder signerBuilder;
-
-        try
-        {
-            signerBuilder = new JcaContentSignerBuilder(signatureName).setSecureRandom(rand);
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new NoSuchAlgorithmException(e.getMessage());
-        }
-
-        if (sigProvider != null)
-        {
-            signerBuilder.setProvider(sigProvider);
-        }
-
-        try
-        {
-            JcaDigestCalculatorProviderBuilder calculatorProviderBuilder = new JcaDigestCalculatorProviderBuilder();
-
-            if (digProvider != null && !digProvider.getName().equalsIgnoreCase("SunRsaSign"))
-            {
-                calculatorProviderBuilder.setProvider(digProvider);
-            }
-
-            JcaSignerInfoGeneratorBuilder builder = new JcaSignerInfoGeneratorBuilder(calculatorProviderBuilder.build());
-
-            builder.setSignedAttributeGenerator(signedAttrGenerator);
-
-            builder.setUnsignedAttributeGenerator(unsignedAttrGenerator);
-
-            try
-            {
-                ContentSigner contentSigner = signerBuilder.build(key);
-
-                if (signerId instanceof X509Certificate)
-                {
-                    addSignerInfoGenerator(builder.build(contentSigner, (X509Certificate)signerId));
-                }
-                else
-                {
-                    addSignerInfoGenerator(builder.build(contentSigner, (byte[])signerId));
-                }
-            }
-            catch (OperatorCreationException e)
-            {
-                if (e.getCause() instanceof NoSuchAlgorithmException)
-                {
-                    throw (NoSuchAlgorithmException)e.getCause();
-                }
-                if (e.getCause() instanceof InvalidKeyException)
-                {
-                    throw (InvalidKeyException)e.getCause();
-                }
-            }
-        }
-        catch (OperatorCreationException e)
-        {
-            throw new NoSuchAlgorithmException("unable to create operators: " + e.getMessage());
-        }
-        catch (CertificateEncodingException e)
-        {
-            throw new IllegalStateException("unable to encode certificate");
-        }
-    }
-
-    /**
-     * @deprecated use addSignerInfoGenerator
-     */
-    public void addSigner(
-        PrivateKey                  key,
-        byte[]                      subjectKeyID,
-        String                      encryptionOID,
-        String                      digestOID,
-        CMSAttributeTableGenerator  signedAttrGenerator,
-        CMSAttributeTableGenerator  unsignedAttrGenerator,
-        Provider                    sigProvider,
-        Provider                    digProvider)
-        throws NoSuchAlgorithmException, InvalidKeyException
-    {
-        doAddSigner(key, subjectKeyID, encryptionOID, digestOID, signedAttrGenerator, unsignedAttrGenerator, sigProvider, digProvider);
-    }
 
     /**
      * generate a signed object that for a CMS Signed Data
@@ -645,18 +112,6 @@
     }
 
     /**
-     * @deprecated use open(ASN1ObjectIdentifier, OutputStream, boolean)
-     */
-    public OutputStream open(
-        OutputStream out,
-        String       eContentType,
-        boolean      encapsulate)
-        throws IOException
-    {
-        return open(out, eContentType, encapsulate, null);
-    }
-
-    /**
      * 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
@@ -672,19 +127,6 @@
     }
 
     /**
-     * @deprecated use open(ASN1ObjectIdenfier, OutputStream, boolean, OutputStream)
-     */
-    public OutputStream open(
-        OutputStream out,
-        String eContentType,
-        boolean      encapsulate,
-        OutputStream dataOutputStream)
-        throws IOException
-    {
-        return open(new ASN1ObjectIdentifier(eContentType), out, encapsulate, dataOutputStream);
-    }
-
-    /**
      * 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
@@ -786,23 +228,6 @@
         return new CmsSignedDataOutputStream(sigStream, eContentType, sGen, sigGen, eiGen);
     }
 
-    // TODO Make public?
-    void generate(
-        OutputStream    out,
-        String          eContentType,
-        boolean         encapsulate,
-        OutputStream    dataOutputStream,
-        CMSProcessable  content)
-        throws CMSException, IOException
-    {
-        OutputStream signedOut = open(out, eContentType, encapsulate, dataOutputStream);
-        if (content != null)
-        {
-            content.write(signedOut);
-        }
-        signedOut.close();
-    }
-
     // RFC3852, section 5.1:
     // IF ((certificates is present) AND
     //    (any certificates with a type of other are present)) OR
@@ -915,7 +340,7 @@
         {
             SignerInfoGenerator s = (SignerInfoGenerator)it.next();
 
-            if (s.getGeneratedVersion().getValue().intValue() == 3)
+            if (s.getGeneratedVersion() == 3)
             {
                 return true;
             }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
index 84369e7..9fe6779 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
@@ -1,12 +1,5 @@
 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;
@@ -17,11 +10,7 @@
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Set;
-import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.DERTaggedObject;
-import org.bouncycastle.asn1.cms.AttributeTable;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
@@ -30,16 +19,12 @@
 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;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.jce.interfaces.GOST3410PrivateKey;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Store;
-import org.bouncycastle.x509.X509AttributeCertificate;
-import org.bouncycastle.x509.X509Store;
 
 public class CMSSignedGenerator
 {
@@ -98,62 +83,11 @@
     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");
-            }
-        }
-        else if (key instanceof GOST3410PrivateKey || "GOST3410".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            encOID = ENCRYPTION_GOST3410;
-        }
-        else if ("ECGOST3410".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            encOID = ENCRYPTION_ECGOST3410;
-        }
-        
-        return encOID;
     }
 
     protected Map getBaseParameters(ASN1ObjectIdentifier contentType, AlgorithmIdentifier digAlgId, byte[] hash)
@@ -165,36 +99,6 @@
         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));
-    }
-
     /**
      * Add a certificate to the certificate set to be included with the generated SignedData message.
      *
@@ -297,40 +201,7 @@
     }
 
     /**
-     * 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.
+     * Add a store of pre-calculated signers to the generator.
      *
      * @param signerStore store of signers
      */
@@ -345,6 +216,11 @@
         }
     }
 
+    /**
+     * Add a generator for a particular signer to this CMS SignedData generator.
+     *
+     * @param infoGen the generator representing the particular signer.
+     */
     public void addSignerInfoGenerator(SignerInfoGenerator infoGen)
     {
          signerGens.add(infoGen);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
index ce20884..2f98e69 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
@@ -1,14 +1,8 @@
 package org.bouncycastle.cms;
 
-import java.io.IOException;
-import java.security.Provider;
-import java.security.cert.CRLException;
-import java.security.cert.CertificateException;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Enumeration;
 import java.util.HashMap;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -35,14 +29,8 @@
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.util.CollectionStore;
 import org.bouncycastle.util.Store;
-import org.bouncycastle.x509.NoSuchStoreException;
-import org.bouncycastle.x509.X509CollectionStoreParameters;
-import org.bouncycastle.x509.X509Store;
-import org.bouncycastle.x509.X509V2AttributeCertificate;
 
 class CMSSignedHelper
 {
@@ -125,23 +113,7 @@
         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
@@ -161,95 +133,6 @@
         return encryptionAlgOID;
     }
 
-    X509Store createAttributeStore(
-        String type,
-        Provider provider,
-        Store certStore)
-        throws NoSuchStoreException, CMSException
-    {
-        try
-        {
-            Collection certHldrs = certStore.getMatches(null);
-            List       certs = new ArrayList(certHldrs.size());
-
-            for (Iterator it = certHldrs.iterator(); it.hasNext();)
-            {
-                certs.add(new X509V2AttributeCertificate(((X509AttributeCertificateHolder)it.next()).getEncoded()));
-            }
-
-            return X509Store.getInstance(
-                         "AttributeCertificate/" +type, new X509CollectionStoreParameters(certs), provider);
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new CMSException("can't setup the X509Store", e);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("can't setup the X509Store", e);
-        }
-    }
-
-    X509Store createCertificateStore(
-        String type,
-        Provider provider,
-        Store certStore)
-        throws NoSuchStoreException, CMSException
-    {
-        try
-        {
-            JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider(provider);
-            Collection certHldrs = certStore.getMatches(null);
-            List       certs = new ArrayList(certHldrs.size());
-
-            for (Iterator it = certHldrs.iterator(); it.hasNext();)
-            {
-                certs.add(converter.getCertificate((X509CertificateHolder)it.next()));
-            }
-
-            return X509Store.getInstance(
-                         "Certificate/" +type, new X509CollectionStoreParameters(certs), provider);
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new CMSException("can't setup the X509Store", e);
-        }
-        catch (CertificateException e)
-        {
-            throw new CMSException("can't setup the X509Store", e);
-        }
-    }
-
-    X509Store createCRLsStore(
-        String type,
-        Provider provider,
-        Store    crlStore)
-        throws NoSuchStoreException, CMSException
-    {
-        try
-        {
-            JcaX509CRLConverter converter = new JcaX509CRLConverter().setProvider(provider);
-            Collection crlHldrs = crlStore.getMatches(null);
-            List       crls = new ArrayList(crlHldrs.size());
-
-            for (Iterator it = crlHldrs.iterator(); it.hasNext();)
-            {
-                crls.add(converter.getCRL((X509CRLHolder)it.next()));
-            }
-
-            return X509Store.getInstance(
-                         "CRL/" +type, new X509CollectionStoreParameters(crls), provider);
-        }
-        catch (IllegalArgumentException e)
-        {
-            throw new CMSException("can't setup the X509Store", e);
-        }
-        catch (CRLException e)
-        {
-            throw new CMSException("can't setup the X509Store", e);
-        }
-    }
-
     AlgorithmIdentifier fixAlgID(AlgorithmIdentifier algId)
     {
         if (algId.getParameters() == null)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
index 743ab8e..dc9c2ee 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
@@ -3,15 +3,6 @@
 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;
@@ -21,7 +12,6 @@
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.BEROctetStringGenerator;
 import org.bouncycastle.asn1.BERSet;
@@ -29,13 +19,9 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.ContentInfo;
-import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
 import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
 import org.bouncycastle.asn1.ocsp.OCSPResponse;
 import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
-import org.bouncycastle.asn1.x509.Certificate;
-import org.bouncycastle.asn1.x509.CertificateList;
-import org.bouncycastle.asn1.x509.TBSCertificate;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
@@ -63,36 +49,6 @@
         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(Certificate.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
     {
@@ -137,35 +93,6 @@
         }
     }
 
-    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
@@ -250,27 +177,6 @@
         return octGen.getOctetOutputStream();
     }
 
-    static TBSCertificate getTBSCertificateStructure(
-        X509Certificate cert)
-    {
-        try
-        {
-            return TBSCertificate.getInstance(
-                ASN1Primitive.fromByteArray(cert.getTBSCertificate()));
-        }
-        catch (Exception e)
-        {
-            throw new IllegalArgumentException(
-                "can't extract TBS structure from this cert");
-        }
-    }
-
-    static IssuerAndSerialNumber getIssuerAndSerialNumber(X509Certificate cert)
-    {
-        TBSCertificate tbsCert = getTBSCertificateStructure(cert);
-        return new IssuerAndSerialNumber(tbsCert.getIssuer(), tbsCert.getSerialNumber().getValue());
-    }
-
     private static ContentInfo readContentInfo(
         ASN1InputStream in)
         throws CMSException
@@ -308,24 +214,6 @@
         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;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java
index 8ba3686..837edd8 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultSignedAttributeTableGenerator.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.cms;
 
 import java.util.Date;
+import java.util.Enumeration;
 import java.util.Hashtable;
 import java.util.Map;
 
@@ -59,7 +60,7 @@
     protected Hashtable createStandardAttributeTable(
         Map parameters)
     {
-        Hashtable std = (Hashtable)table.clone();
+        Hashtable std = copyHashTable(table);
 
         if (!std.containsKey(CMSAttributes.contentType))
         {
@@ -103,4 +104,18 @@
     {
         return new AttributeTable(createStandardAttributeTable(parameters));
     }
+
+    private static Hashtable copyHashTable(Hashtable paramsMap)
+    {
+        Hashtable newTable = new Hashtable();
+
+        Enumeration keys = paramsMap.keys();
+        while (keys.hasMoreElements())
+        {
+            Object key = keys.nextElement();
+            newTable.put(key, paramsMap.get(key));
+        }
+
+        return newTable;
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/KEKRecipientInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/KEKRecipientInformation.java
index 4e1b8cd..62c6529 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/KEKRecipientInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/KEKRecipientInformation.java
@@ -1,18 +1,10 @@
 package org.bouncycastle.cms;
 
 import java.io.IOException;
-import java.security.Key;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-
-import javax.crypto.SecretKey;
 
 import org.bouncycastle.asn1.cms.KEKIdentifier;
 import org.bouncycastle.asn1.cms.KEKRecipientInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceKEKAuthenticatedRecipient;
-import org.bouncycastle.cms.jcajce.JceKEKEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JceKEKRecipient;
 
 /**
  * the RecipientInfo class for a recipient who has been sent a message
@@ -38,52 +30,6 @@
         this.rid = new KEKRecipientId(kekId.getKeyIdentifier().getOctets());
     }
 
-    /**
-     * decrypt the content and return an input stream.
-     */
-    public CMSTypedStream getContentStream(
-        Key      key,
-        String   prov)
-        throws CMSException, NoSuchProviderException
-    {
-        return getContentStream(key, CMSUtils.getProvider(prov));
-    }
-
-    /**
-     * decrypt the content and return an input stream.
-     * @deprecated use getContentStream(Recipient)
-     */
-    public CMSTypedStream getContentStream(
-        Key      key,
-        Provider prov)
-        throws CMSException
-    {
-        try
-        {
-            JceKEKRecipient recipient;
-
-            if (secureReadable instanceof CMSEnvelopedHelper.CMSEnvelopedSecureReadable)
-            {
-                recipient = new JceKEKEnvelopedRecipient((SecretKey)key);
-            }
-            else
-            {
-                recipient = new JceKEKAuthenticatedRecipient((SecretKey)key);
-            }
-
-            if (prov != null)
-            {
-                recipient.setProvider(prov);
-            }
-
-            return getContentStream(recipient);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("encoding error: " + e.getMessage(), e);
-        }
-    }
-
     protected RecipientOperator getRecipientOperator(Recipient recipient)
         throws CMSException, IOException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInformation.java
index 51917da..16c26bd 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInformation.java
@@ -1,10 +1,6 @@
 package org.bouncycastle.cms;
 
 import java.io.IOException;
-import java.security.Key;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.Provider;
 import java.util.List;
 
 import org.bouncycastle.asn1.ASN1OctetString;
@@ -19,9 +15,6 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeAuthenticatedRecipient;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipient;
 
 /**
  * the RecipientInfo class for a recipient who has been sent a message
@@ -126,57 +119,6 @@
         throw new CMSException("No support for 'originator' as IssuerAndSerialNumber or SubjectKeyIdentifier");
     }
 
-    /**
-     * decrypt the content and return it
-     * @deprecated use getContentStream(Recipient) method
-     */
-    public CMSTypedStream getContentStream(
-        Key key,
-        String prov)
-        throws CMSException, NoSuchProviderException
-    {
-        return getContentStream(key, CMSUtils.getProvider(prov));
-    }
-
-    /**
-     * decrypt the content and return it
-     * @deprecated use getContentStream(Recipient) method
-     */
-    public CMSTypedStream getContentStream(
-        Key key,
-        Provider prov)
-        throws CMSException
-    {
-        try
-        {
-            JceKeyAgreeRecipient recipient;
-
-            if (secureReadable instanceof CMSEnvelopedHelper.CMSEnvelopedSecureReadable)
-            {
-                recipient = new JceKeyAgreeEnvelopedRecipient((PrivateKey)key);
-            }
-            else
-            {
-                recipient = new JceKeyAgreeAuthenticatedRecipient((PrivateKey)key);
-            }
-
-            if (prov != null)
-            {
-                recipient.setProvider(prov);
-                if (prov.getName().equalsIgnoreCase("SunJCE"))
-                {
-                    recipient.setContentProvider((String)null);    // need to fall back to generic search
-                }
-            }
-
-            return getContentStream(recipient);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("encoding error: " + e.getMessage(), e);
-        }
-    }
-
     protected RecipientOperator getRecipientOperator(Recipient recipient)
         throws CMSException, IOException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/KeyTransRecipientInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/KeyTransRecipientInformation.java
index a1180b4..d59f4b3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/KeyTransRecipientInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/KeyTransRecipientInformation.java
@@ -1,20 +1,10 @@
 package org.bouncycastle.cms;
 
-import java.io.IOException;
-import java.security.Key;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.Provider;
-
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
 import org.bouncycastle.asn1.cms.KeyTransRecipientInfo;
 import org.bouncycastle.asn1.cms.RecipientIdentifier;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceKeyTransAuthenticatedRecipient;
-import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JceKeyTransRecipient;
-
 
 /**
  * the KeyTransRecipientInformation class for a recipient who has been sent a secret
@@ -52,57 +42,6 @@
         }
     }
 
-    /**
-     * decrypt the content and return it
-     * @deprecated use getContentStream(Recipient) method
-     */
-    public CMSTypedStream getContentStream(
-        Key key,
-        String prov)
-        throws CMSException, NoSuchProviderException
-    {
-        return getContentStream(key, CMSUtils.getProvider(prov));
-    }
-
-    /**
-     * decrypt the content and return it
-     * @deprecated use getContentStream(Recipient) method
-     */
-    public CMSTypedStream getContentStream(
-        Key key,
-        Provider prov)
-        throws CMSException
-    {
-        try
-        {
-            JceKeyTransRecipient recipient;
-
-            if (secureReadable instanceof CMSEnvelopedHelper.CMSEnvelopedSecureReadable)
-            {
-                recipient = new JceKeyTransEnvelopedRecipient((PrivateKey)key);
-            }
-            else
-            {
-                recipient = new JceKeyTransAuthenticatedRecipient((PrivateKey)key);
-            }
-
-            if (prov != null)
-            {
-                recipient.setProvider(prov);
-                if (prov.getName().equalsIgnoreCase("SunJCE"))
-                {
-                    recipient.setContentProvider((String)null);    // need to fall back to generic search
-                }
-            }
-
-            return getContentStream(recipient);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("encoding error: " + e.getMessage(), e);
-        }
-    }
-
     protected RecipientOperator getRecipientOperator(Recipient recipient)
         throws CMSException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS5Scheme2PBEKey.java b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS5Scheme2PBEKey.java
deleted file mode 100644
index b5be483..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS5Scheme2PBEKey.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.bouncycastle.cms;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-
-import org.bouncycastle.crypto.PBEParametersGenerator;
-import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-/**
- * PKCS5 scheme-2 - password converted to bytes assuming ASCII.
- */
-public class PKCS5Scheme2PBEKey
-    extends CMSPBEKey
-{
-    public PKCS5Scheme2PBEKey(char[] password, byte[] salt, int iterationCount)
-    {
-        super(password, salt, iterationCount);
-    }
-
-    public PKCS5Scheme2PBEKey(char[] password, AlgorithmParameters pbeParams)
-        throws InvalidAlgorithmParameterException
-    {
-        super(password, getParamSpec(pbeParams));
-    }
-
-    byte[] getEncoded(String algorithmOid)
-    {
-        PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator();
-
-        gen.init(PBEParametersGenerator.PKCS5PasswordToBytes(this.getPassword()), this.getSalt(), this.getIterationCount());
-
-        return ((KeyParameter)gen.generateDerivedParameters(CMSEnvelopedHelper.INSTANCE.getKeySize(algorithmOid))).getKey();
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS5Scheme2UTF8PBEKey.java b/bcpkix/src/main/java/org/bouncycastle/cms/PKCS5Scheme2UTF8PBEKey.java
deleted file mode 100644
index 436ba66..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/PKCS5Scheme2UTF8PBEKey.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.bouncycastle.cms;
-
-import java.security.AlgorithmParameters;
-import java.security.InvalidAlgorithmParameterException;
-
-import org.bouncycastle.crypto.PBEParametersGenerator;
-import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
-import org.bouncycastle.crypto.params.KeyParameter;
-
-/**
- * PKCS5 scheme-2 - password converted to bytes using UTF-8.
- */
-public class PKCS5Scheme2UTF8PBEKey
-    extends CMSPBEKey
-{
-    public PKCS5Scheme2UTF8PBEKey(char[] password, byte[] salt, int iterationCount)
-    {
-        super(password, salt, iterationCount);
-    }
-
-    public PKCS5Scheme2UTF8PBEKey(char[] password, AlgorithmParameters pbeParams)
-        throws InvalidAlgorithmParameterException
-    {
-        super(password, getParamSpec(pbeParams));
-    }
-
-    byte[] getEncoded(String algorithmOid)
-    {
-        PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator();
-
-        gen.init(PBEParametersGenerator.PKCS5PasswordToUTF8Bytes(this.getPassword()), this.getSalt(), this.getIterationCount());
-
-        return ((KeyParameter)gen.generateDerivedParameters(CMSEnvelopedHelper.INSTANCE.getKeySize(algorithmOid))).getKey();
-    }
-}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/PasswordRecipientInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/PasswordRecipientInformation.java
index 4517ad6..d7639e9 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/PasswordRecipientInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/PasswordRecipientInformation.java
@@ -1,10 +1,6 @@
 package org.bouncycastle.cms;
 
 import java.io.IOException;
-import java.security.AlgorithmParameters;
-import java.security.Key;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -12,10 +8,6 @@
 import org.bouncycastle.asn1.cms.PasswordRecipientInfo;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceAlgorithmIdentifierConverter;
-import org.bouncycastle.cms.jcajce.JcePasswordAuthenticatedRecipient;
-import org.bouncycastle.cms.jcajce.JcePasswordEnvelopedRecipient;
-import org.bouncycastle.cms.jcajce.JcePasswordRecipient;
 import org.bouncycastle.crypto.PBEParametersGenerator;
 import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
 import org.bouncycastle.crypto.params.KeyParameter;
@@ -110,88 +102,6 @@
         return info.getKeyDerivationAlgorithm();
     }
 
-    /**
-     * return an AlgorithmParameters object representing the parameters to the
-     * key derivation algorithm to the recipient.
-     *
-     * @return AlgorithmParameters object, null if there aren't any.
-     * @deprecated use getKeyDerivationAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getKeyDerivationAlgParameters(String provider)
-        throws NoSuchProviderException
-    {
-        return getKeyDerivationAlgParameters(CMSUtils.getProvider(provider));
-    }
-    
-   /**
-     * return an AlgorithmParameters object representing the parameters to the
-     * key derivation algorithm to the recipient.
-     *
-     * @return AlgorithmParameters object, null if there aren't any.
-    *  @deprecated use getKeyDerivationAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getKeyDerivationAlgParameters(Provider provider)
-    {
-        try
-        {
-            return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(info.getKeyDerivationAlgorithm());
-        }
-        catch (Exception e)
-        {
-            throw new RuntimeException("exception getting encryption parameters " + e);
-        }
-    }
-
-    /**
-     * decrypt the content and return an input stream.
-     * @deprecated use getContentStream(Recipient)
-     */
-    public CMSTypedStream getContentStream(
-        Key key,
-        String   prov)
-        throws CMSException, NoSuchProviderException
-    {
-        return getContentStream(key, CMSUtils.getProvider(prov));
-    }
-
-    /**
-     * decrypt the content and return an input stream.
-     * @deprecated use getContentStream(Recipient)
-     */
-    public CMSTypedStream getContentStream(
-        Key key,
-        Provider prov)
-        throws CMSException
-    {
-        try
-        {
-            CMSPBEKey pbeKey = (CMSPBEKey)key;
-            JcePasswordRecipient recipient;
-
-            if (secureReadable instanceof CMSEnvelopedHelper.CMSEnvelopedSecureReadable)
-            {
-                recipient = new JcePasswordEnvelopedRecipient(pbeKey.getPassword());
-            }
-            else
-            {
-                recipient = new JcePasswordAuthenticatedRecipient(pbeKey.getPassword());
-            }
-
-            recipient.setPasswordConversionScheme((pbeKey instanceof PKCS5Scheme2UTF8PBEKey) ? PasswordRecipient.PKCS5_SCHEME2_UTF8 : PasswordRecipient.PKCS5_SCHEME2);
-
-            if (prov != null)
-            {
-                recipient.setProvider(prov);
-            }
-
-            return getContentStream(recipient);
-        }
-        catch (IOException e)
-        {
-            throw new CMSException("encoding error: " + e.getMessage(), e);
-        }
-    }
-
     protected RecipientOperator getRecipientOperator(Recipient recipient)
         throws CMSException, IOException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/RecipientInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/RecipientInformation.java
index 5129881..86f9fa3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/RecipientInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/RecipientInformation.java
@@ -2,15 +2,10 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
-import java.security.AlgorithmParameters;
-import java.security.Key;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.jcajce.JceAlgorithmIdentifierConverter;
 import org.bouncycastle.util.io.Streams;
 
 public abstract class RecipientInformation
@@ -71,7 +66,7 @@
      */
     public String getKeyEncryptionAlgOID()
     {
-        return keyEncAlg.getObjectId().getId();
+        return keyEncAlg.getAlgorithm().getId();
     }
 
     /**
@@ -93,68 +88,6 @@
     }
 
     /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the key this recipient holds.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws CMSException            if the algorithm cannot be found, or the parameters can't be parsed.
-     * @throws NoSuchProviderException if the provider cannot be found.
-     * @deprecated use getKeyEncryptionAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getKeyEncryptionAlgorithmParameters(
-        String provider)
-        throws CMSException, NoSuchProviderException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(keyEncAlg);
-    }
-
-    /**
-     * Return an AlgorithmParameters object giving the encryption parameters
-     * used to encrypt the key this recipient holds.
-     *
-     * @param provider the provider to generate the parameters for.
-     * @return the parameters object, null if there is not one.
-     * @throws CMSException if the algorithm cannot be found, or the parameters can't be parsed.
-     * @deprecated use getKeyEncryptionAlgorithm and JceAlgorithmIdentifierConverter().
-     */
-    public AlgorithmParameters getKeyEncryptionAlgorithmParameters(
-        Provider provider)
-        throws CMSException
-    {
-        return new JceAlgorithmIdentifierConverter().setProvider(provider).getAlgorithmParameters(keyEncAlg);
-    }
-
-    /**
-     * @deprecated use getContent(Recipient)
-     */
-    public byte[] getContent(
-        Key key,
-        String provider)
-        throws CMSException, NoSuchProviderException
-    {
-        return getContent(key, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * @deprecated use getContent(Recipient)
-     */
-    public byte[] getContent(
-        Key key,
-        Provider provider)
-        throws CMSException
-    {
-        try
-        {
-            return CMSUtils.streamToByteArray(getContentStream(key, provider).getContentStream());
-        }
-        catch (IOException e)
-        {
-            throw new RuntimeException("unable to parse internal stream: " + e);
-        }
-    }
-
-    /**
      * Return the content digest calculated during the read of the content if one has been generated. This will
      * only happen if we are dealing with authenticated data and authenticated attributes are present.
      *
@@ -223,24 +156,6 @@
     }
 
     /**
-     * decrypt the content and return it
-     * @deprecated use getContentStream(Recipient) method
-     */
-    public CMSTypedStream getContentStream(Key key, String provider)
-        throws CMSException, NoSuchProviderException
-    {
-        return getContentStream(key, CMSUtils.getProvider(provider));
-    }
-
-    /**
-     * decrypt the content and return it
-     * @deprecated use getContentStream(Recipient) method
-     */
-    public abstract CMSTypedStream getContentStream(Key key, Provider provider)
-        throws CMSException;
-
-
-    /**
      * Return a CMSTypedStream representing the content in the EnvelopedData after recovering the content
      * encryption/MAC key using the passed in Recipient.
      *
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
index e378629..f264729 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInfoGenerator.java
@@ -7,7 +7,6 @@
 import java.util.Map;
 
 import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.DEROctetString;
@@ -23,6 +22,7 @@
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.DigestCalculatorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.io.TeeOutputStream;
 
 public class SignerInfoGenerator
@@ -126,9 +126,9 @@
         return signerIdentifier;
     }
 
-    public ASN1Integer getGeneratedVersion()
+    public int getGeneratedVersion()
     {
-        return new ASN1Integer(signerIdentifier.isTagged() ? 3 : 1);
+        return signerIdentifier.isTagged() ? 3 : 1;
     }
 
     public boolean hasAssociatedCertificate()
@@ -221,7 +221,7 @@
             if (unsAttrGen != null)
             {
                 Map parameters = getBaseParameters(contentType, digestAlg, calculatedDigest);
-                parameters.put(CMSAttributeTableGenerator.SIGNATURE, sigBytes.clone());
+                parameters.put(CMSAttributeTableGenerator.SIGNATURE, Arrays.clone(sigBytes));
 
                 AttributeTable unsigned = unsAttrGen.getAttributes(Collections.unmodifiableMap(parameters));
 
@@ -265,7 +265,7 @@
         }
 
         param.put(CMSAttributeTableGenerator.DIGEST_ALGORITHM_IDENTIFIER, digAlgId);
-        param.put(CMSAttributeTableGenerator.DIGEST,  hash.clone());
+        param.put(CMSAttributeTableGenerator.DIGEST,  Arrays.clone(hash));
         return param;
     }
 
@@ -273,7 +273,7 @@
     {
         if (calculatedDigest != null)
         {
-            return (byte[])calculatedDigest.clone();
+            return Arrays.clone(calculatedDigest);
         }
 
         return null;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
index bd9703a..7e178d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
@@ -2,13 +2,6 @@
 
 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;
@@ -33,13 +26,10 @@
 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;
 import org.bouncycastle.util.io.TeeOutputStream;
 
@@ -172,7 +162,7 @@
             throw new IllegalStateException("method can only be called after verify.");
         }
         
-        return (byte[])resultDigest.clone();
+        return Arrays.clone(resultDigest);
     }
     
     /**
@@ -232,7 +222,7 @@
      */
     public byte[] getSignature()
     {
-        return (byte[])signature.clone();
+        return Arrays.clone(signature);
     }
 
     /**
@@ -318,42 +308,6 @@
         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
@@ -555,75 +509,6 @@
     }
 
     /**
-     * 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.
      *
@@ -654,17 +539,6 @@
     }
 
     /**
-     * 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.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
index bd36b73..104b16f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
@@ -1,14 +1,19 @@
 package org.bouncycastle.cms.jcajce;
 
+import java.io.IOException;
+import java.security.AlgorithmParameters;
 import java.security.Provider;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 
+import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
 import org.bouncycastle.asn1.x509.Certificate;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.TBSCertificateStructure;
-import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.jcajce.JcaJceUtils;
 
 class CMSUtils
 {
@@ -30,7 +35,7 @@
 
     static byte[] getSubjectKeyId(X509Certificate cert)
     {
-        byte[] ext = cert.getExtensionValue(X509Extension.subjectKeyIdentifier.getId());
+        byte[] ext = cert.getExtensionValue(Extension.subjectKeyIdentifier.getId());
 
         if (ext != null)
         {
@@ -66,4 +71,29 @@
         }
     }
 
+    static ASN1Encodable extractParameters(AlgorithmParameters params)
+        throws CMSException
+    {
+        try
+        {
+            return JcaJceUtils.extractParameters(params);
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("cannot extract parameters: " + e.getMessage(), e);
+        }
+    }
+
+    static void loadParameters(AlgorithmParameters params, ASN1Encodable sParams)
+        throws CMSException
+    {
+        try
+        {
+            JcaJceUtils.loadParameters(params, sParams);
+        }
+        catch (IOException e)
+        {
+            throw new CMSException("error encoding algorithm parameters.", e);
+        }
+    }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
index 5f3958f..b081051 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.cms.jcajce;
 
-import java.io.IOException;
 import java.security.AlgorithmParameterGenerator;
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
@@ -32,7 +31,6 @@
 import org.bouncycastle.asn1.ASN1Null;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
-import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
@@ -41,12 +39,16 @@
 import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
 import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
 import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.operator.SecretKeySizeProvider;
 import org.bouncycastle.operator.SymmetricKeyUnwrapper;
 import org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper;
 
-class EnvelopedDataHelper
+public class EnvelopedDataHelper
 {
+    protected static final SecretKeySizeProvider KEY_SIZE_PROVIDER = DefaultSecretKeySizeProvider.INSTANCE;
+
     protected static final Map BASE_CIPHER_NAMES = new HashMap();
     protected static final Map CIPHER_ALG_NAMES = new HashMap();
     protected static final Map MAC_ALG_NAMES = new HashMap();
@@ -64,8 +66,10 @@
         BASE_CIPHER_NAMES.put(CMSAlgorithm.CAMELLIA192_CBC, "Camellia");
         BASE_CIPHER_NAMES.put(CMSAlgorithm.CAMELLIA256_CBC, "Camellia");
         BASE_CIPHER_NAMES.put(CMSAlgorithm.SEED_CBC, "SEED");
+        BASE_CIPHER_NAMES.put(PKCSObjectIdentifiers.rc4, "RC4");
 
         CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_CBC,  "DES/CBC/PKCS5Padding");
+        CIPHER_ALG_NAMES.put(CMSAlgorithm.RC2_CBC,  "RC2/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_EDE3_CBC,  "DESEDE/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.AES128_CBC,  "AES/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.AES192_CBC,  "AES/CBC/PKCS5Padding");
@@ -76,6 +80,7 @@
         CIPHER_ALG_NAMES.put(CMSAlgorithm.CAMELLIA192_CBC, "Camellia/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.CAMELLIA256_CBC, "Camellia/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.SEED_CBC, "SEED/CBC/PKCS5Padding");
+        CIPHER_ALG_NAMES.put(PKCSObjectIdentifiers.rc4, "RC4");
 
         MAC_ALG_NAMES.put(CMSAlgorithm.DES_EDE3_CBC,  "DESEDEMac");
         MAC_ALG_NAMES.put(CMSAlgorithm.AES128_CBC,  "AESMac");
@@ -156,7 +161,7 @@
         throw new IllegalArgumentException("unknown generic key type");
     }
 
-    Key getJceKey(ASN1ObjectIdentifier algorithm, GenericKey key)
+    public Key getJceKey(ASN1ObjectIdentifier algorithm, GenericKey key)
     {
         if (key.getRepresentation() instanceof Key)
         {
@@ -171,6 +176,33 @@
         throw new IllegalArgumentException("unknown generic key type");
     }
 
+    public void keySizeCheck(AlgorithmIdentifier keyAlgorithm, Key key)
+        throws CMSException
+    {
+        int expectedKeySize = EnvelopedDataHelper.KEY_SIZE_PROVIDER.getKeySize(keyAlgorithm);
+        if (expectedKeySize > 0)
+        {
+            byte[] keyEnc = null;
+
+            try
+            {
+                keyEnc = key.getEncoded();
+            }
+            catch (Exception e)
+            {
+                // ignore - we're using a HSM...
+            }
+
+            if (keyEnc != null)
+            {
+                if (keyEnc.length * 8 != expectedKeySize)
+                {
+                    throw new CMSException("Expected key size for algorithm OID not found in recipient.");
+                }
+            }
+        }
+    }
+
     Cipher createCipher(ASN1ObjectIdentifier algorithm)
         throws CMSException
     {
@@ -294,7 +326,7 @@
         return helper.createAlgorithmParameterGenerator(algorithm.getId());
     }
 
-    Cipher createContentCipher(final Key sKey, final AlgorithmIdentifier encryptionAlgID)
+    public Cipher createContentCipher(final Key sKey, final AlgorithmIdentifier encryptionAlgID)
         throws CMSException
     {
         return (Cipher)execute(new JCECallback()
@@ -314,14 +346,7 @@
                     {
                         AlgorithmParameters params = createAlgorithmParameters(encryptionAlgID.getAlgorithm());
 
-                        try
-                        {
-                            params.init(sParams.toASN1Primitive().getEncoded(), "ASN.1");
-                        }
-                        catch (IOException e)
-                        {
-                            throw new CMSException("error decoding algorithm parameters.", e);
-                        }
+                        CMSUtils.loadParameters(params, sParams);
 
                         cipher.init(Cipher.DECRYPT_MODE, sKey, params);
                     }
@@ -383,14 +408,7 @@
                     {
                         AlgorithmParameters params = createAlgorithmParameters(macAlgId.getAlgorithm());
 
-                        try
-                        {
-                            params.init(sParams.toASN1Primitive().getEncoded(), "ASN.1");
-                        }
-                        catch (IOException e)
-                        {
-                            throw new CMSException("error decoding algorithm parameters.", e);
-                        }
+                        CMSUtils.loadParameters(params, sParams);
 
                         mac.init(sKey, params.getParameterSpec(IvParameterSpec.class));
                     }
@@ -491,7 +509,7 @@
         {
             AlgorithmParameterGenerator pGen = createAlgorithmParameterGenerator(encryptionOID);
 
-            if (encryptionOID.equals(CMSEnvelopedDataGenerator.RC2_CBC))
+            if (encryptionOID.equals(CMSAlgorithm.RC2_CBC))
             {
                 byte[]  iv = new byte[8];
 
@@ -525,14 +543,7 @@
         ASN1Encodable asn1Params;
         if (params != null)
         {
-            try
-            {
-                asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1"));
-            }
-            catch (IOException e)
-            {
-                throw new CMSException("cannot encode parameters: " + e.getMessage(), e);
-            }
+            asn1Params = CMSUtils.extractParameters(params);
         }
         else
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAlgorithmIdentifierConverter.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAlgorithmIdentifierConverter.java
index bb9e064..59928f4 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAlgorithmIdentifierConverter.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceAlgorithmIdentifierConverter.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.cms.jcajce;
 
 
-import java.io.IOException;
 import java.security.AlgorithmParameters;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
@@ -49,7 +48,7 @@
         {
             AlgorithmParameters params = helper.createAlgorithmParameters(algorithmIdentifier.getAlgorithm());
 
-            params.init(parameters.toASN1Primitive().getEncoded(), "ASN.1");
+            CMSUtils.loadParameters(params, algorithmIdentifier.getParameters());
 
             return params;
         }
@@ -57,10 +56,6 @@
         {
             throw new CMSException("can't find parameters for algorithm", e);
         }
-        catch (IOException e)
-        {
-            throw new CMSException("can't parse parameters", e);
-        }
         catch (NoSuchProviderException e)
         {
             throw new CMSException("can't find provider for algorithm", e);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java
index 89d2c65..93d8b72 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceCMSContentEncryptorBuilder.java
@@ -5,8 +5,6 @@
 import java.security.GeneralSecurityException;
 import java.security.Provider;
 import java.security.SecureRandom;
-import java.util.HashMap;
-import java.util.Map;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherOutputStream;
@@ -14,40 +12,19 @@
 import javax.crypto.SecretKey;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.operator.SecretKeySizeProvider;
 import org.bouncycastle.operator.jcajce.JceGenericKey;
-import org.bouncycastle.util.Integers;
 
 public class JceCMSContentEncryptorBuilder
 {
-    private static Map keySizes = new HashMap();
+    private static final SecretKeySizeProvider KEY_SIZE_PROVIDER = DefaultSecretKeySizeProvider.INSTANCE;
 
-    static
-    {
-        keySizes.put(CMSAlgorithm.AES128_CBC, Integers.valueOf(128));
-        keySizes.put(CMSAlgorithm.AES192_CBC, Integers.valueOf(192));
-        keySizes.put(CMSAlgorithm.AES256_CBC, Integers.valueOf(256));
-
-        keySizes.put(CMSAlgorithm.CAMELLIA128_CBC, Integers.valueOf(128));
-        keySizes.put(CMSAlgorithm.CAMELLIA192_CBC, Integers.valueOf(192));
-        keySizes.put(CMSAlgorithm.CAMELLIA256_CBC, Integers.valueOf(256));
-    }
-
-    private static int getKeySize(ASN1ObjectIdentifier oid)
-    {
-        Integer size = (Integer)keySizes.get(oid);
-
-        if (size != null)
-        {
-            return size.intValue();
-        }
-
-        return -1;
-    }
 
     private final ASN1ObjectIdentifier encryptionOID;
     private final int                  keySize;
@@ -57,13 +34,30 @@
 
     public JceCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
     {
-        this(encryptionOID, getKeySize(encryptionOID));
+        this(encryptionOID, KEY_SIZE_PROVIDER.getKeySize(encryptionOID));
     }
 
     public JceCMSContentEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
     {
         this.encryptionOID = encryptionOID;
         this.keySize = keySize;
+
+        int fixedSize = KEY_SIZE_PROVIDER.getKeySize(encryptionOID);
+
+        if (encryptionOID.equals(PKCSObjectIdentifiers.des_EDE3_CBC))
+        {
+            if (keySize != 168 && keySize != fixedSize)
+            {
+                throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder.");
+            }
+        }
+        else
+        {
+            if (fixedSize > 0 && fixedSize != keySize)
+            {
+                throw new IllegalArgumentException("incorrect keySize for encryptionOID passed to builder.");
+            }
+        }
     }
 
     public JceCMSContentEncryptorBuilder setProvider(Provider provider)
@@ -116,6 +110,10 @@
             }
             else
             {
+                if (encryptionOID.equals(PKCSObjectIdentifiers.des_EDE3_CBC) && keySize == 192)
+                {
+                    keySize = 168;
+                }
                 keyGen.init(keySize, random);
             }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKRecipient.java
index a01e279..d0e4164 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKEKRecipient.java
@@ -18,6 +18,7 @@
 
     protected EnvelopedDataHelper helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper());
     protected EnvelopedDataHelper contentHelper = helper;
+    protected boolean validateKeySize = false;
 
     public JceKEKRecipient(SecretKey recipientKey)
     {
@@ -78,14 +79,37 @@
         return this;
     }
 
-    protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier contentEncryptionAlgorithm, byte[] encryptedContentEncryptionKey)
+    /**
+     * Set validation of retrieved key sizes against the algorithm parameters for the encrypted key where possible - default is off.
+     * <p>
+     * This setting will not have any affect if the encryption algorithm in the recipient does not specify a particular key size, or
+     * if the unwrapper is a HSM and the byte encoding of the unwrapped secret key is not available.
+     * </p>
+     * @param doValidate true if unwrapped key's should be validated against the content encryption algorithm, false otherwise.
+     * @return this recipient.
+     */
+    public JceKEKRecipient setKeySizeValidation(boolean doValidate)
+    {
+        this.validateKeySize = doValidate;
+
+        return this;
+    }
+
+    protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedContentEncryptionKey)
         throws CMSException
     {
         SymmetricKeyUnwrapper unwrapper = helper.createSymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey);
 
         try
         {
-            return helper.getJceKey(contentEncryptionAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(contentEncryptionAlgorithm, encryptedContentEncryptionKey));
+            Key key =  helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedContentEncryptionKey));
+
+            if (validateKeySize)
+            {
+                helper.keySizeCheck(encryptedKeyAlgorithm, key);
+            }
+
+            return key;
         }
         catch (OperatorException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
index 788af8d..a457ede 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
@@ -22,6 +22,7 @@
     protected EnvelopedDataHelper helper = new EnvelopedDataHelper(new DefaultJcaJceExtHelper());
     protected EnvelopedDataHelper contentHelper = helper;
     protected Map extraMappings = new HashMap();
+    protected boolean validateKeySize = false;
 
     public JceKeyTransRecipient(PrivateKey recipientKey)
     {
@@ -105,6 +106,22 @@
         return this;
     }
 
+    /**
+     * Set validation of retrieved key sizes against the algorithm parameters for the encrypted key where possible - default is off.
+     * <p>
+     * This setting will not have any affect if the encryption algorithm in the recipient does not specify a particular key size, or
+     * if the unwrapper is a HSM and the byte encoding of the unwrapped secret key is not available.
+     * </p>
+     * @param doValidate true if unwrapped key's should be validated against the content encryption algorithm, false otherwise.
+     * @return this recipient.
+     */
+    public JceKeyTransRecipient setKeySizeValidation(boolean doValidate)
+    {
+        this.validateKeySize = doValidate;
+
+        return this;
+    }
+
     protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedEncryptionKey)
         throws CMSException
     {
@@ -122,7 +139,14 @@
 
         try
         {
-            return helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+            Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+
+            if (validateKeySize)
+            {
+                helper.keySizeCheck(encryptedKeyAlgorithm, key);
+            }
+
+            return key;
         }
         catch (OperatorException e)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
index 73733c7..60a2ff2 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
@@ -7,6 +7,7 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cms.KeyTransRecipientInfoGenerator;
 import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper;
@@ -17,7 +18,7 @@
     public JceKeyTransRecipientInfoGenerator(X509Certificate recipientCert)
         throws CertificateEncodingException
     {
-        super(new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), new JceAsymmetricKeyWrapper(recipientCert.getPublicKey()));
+        super(new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), new JceAsymmetricKeyWrapper(recipientCert));
     }
 
     public JceKeyTransRecipientInfoGenerator(byte[] subjectKeyIdentifier, PublicKey publicKey)
@@ -25,6 +26,30 @@
         super(subjectKeyIdentifier, new JceAsymmetricKeyWrapper(publicKey));
     }
 
+    /**
+     * Create a generator overriding the algorithm type implied by the public key in the certificate passed in.
+     *
+     * @param recipientCert certificate carrying the public key.
+     * @param algorithmIdentifier the identifier and parameters for the encryption algorithm to be used.
+     */
+    public JceKeyTransRecipientInfoGenerator(X509Certificate recipientCert, AlgorithmIdentifier algorithmIdentifier)
+        throws CertificateEncodingException
+    {
+        super(new IssuerAndSerialNumber(new JcaX509CertificateHolder(recipientCert).toASN1Structure()), new JceAsymmetricKeyWrapper(algorithmIdentifier, recipientCert.getPublicKey()));
+    }
+
+    /**
+     * Create a generator overriding the algorithm type implied by the public key passed in.
+     *
+     * @param subjectKeyIdentifier  the subject key identifier value to associate with the public key.
+     * @param algorithmIdentifier  the identifier and parameters for the encryption algorithm to be used.
+     * @param publicKey the public key to use.
+     */
+    public JceKeyTransRecipientInfoGenerator(byte[] subjectKeyIdentifier, AlgorithmIdentifier algorithmIdentifier, PublicKey publicKey)
+    {
+        super(subjectKeyIdentifier, new JceAsymmetricKeyWrapper(algorithmIdentifier, publicKey));
+    }
+
     public JceKeyTransRecipientInfoGenerator setProvider(String providerName)
     {
         ((JceAsymmetricKeyWrapper)this.wrapper).setProvider(providerName);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/ZlibExpanderProvider.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/ZlibExpanderProvider.java
index 107a0ef..15729a7 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/ZlibExpanderProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/ZlibExpanderProvider.java
@@ -15,6 +15,9 @@
 {
     private final long limit;
 
+    /**
+     * Base constructor. Create an expander which will not limit the size of any objects expanded in the stream.
+     */
     public ZlibExpanderProvider()
     {
         this.limit = -1;
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/package.html b/bcpkix/src/main/java/org/bouncycastle/cms/package.html
deleted file mode 100644
index 644e862..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-A package for processing RFC 3852 Cryptographic Message Syntax (CMS) objects - also referred to as PKCS#7 (formerly RFC 2630, 3369). 
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/AllTests.java
index dc81f5a..9cc2b0e 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/AllTests.java
@@ -18,21 +18,13 @@
     {   
         TestSuite suite = new TestSuite("CMS tests");
 
-        suite.addTest(AuthenticatedDataTest.suite());
-        suite.addTest(AuthenticatedDataStreamTest.suite());
-        suite.addTest(CompressedDataTest.suite());
         suite.addTest(NewCompressedDataTest.suite());
-        suite.addTest(SignedDataTest.suite());
         suite.addTest(NewSignedDataTest.suite());
-        suite.addTest(EnvelopedDataTest.suite());
         suite.addTest(NewEnvelopedDataTest.suite());
         suite.addTest(NewAuthenticatedDataTest.suite());
         suite.addTest(NewAuthenticatedDataStreamTest.suite());
-        suite.addTest(CompressedDataStreamTest.suite());
         suite.addTest(NewCompressedDataStreamTest.suite());
-        suite.addTest(SignedDataStreamTest.suite());
         suite.addTest(NewSignedDataStreamTest.suite());
-        suite.addTest(EnvelopedDataStreamTest.suite());
         suite.addTest(NewEnvelopedDataStreamTest.suite());
 
         suite.addTest(MiscDataStreamTest.suite());
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/AuthenticatedDataStreamTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/AuthenticatedDataStreamTest.java
deleted file mode 100644
index fe056e6..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/AuthenticatedDataStreamTest.java
+++ /dev/null
@@ -1,142 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.security.KeyPair;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.cms.CMSAuthenticatedDataGenerator;
-import org.bouncycastle.cms.CMSAuthenticatedDataParser;
-import org.bouncycastle.cms.CMSAuthenticatedDataStreamGenerator;
-import org.bouncycastle.cms.RecipientInformation;
-import org.bouncycastle.cms.RecipientInformationStore;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-
-public class AuthenticatedDataStreamTest
-    extends TestCase
-{
-    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
-
-    private static String          _signDN;
-    private static KeyPair _signKP;
-    private static X509Certificate _signCert;
-
-    private static String          _origDN;
-    private static KeyPair         _origKP;
-    private static X509Certificate _origCert;
-
-    private static String          _reciDN;
-    private static KeyPair         _reciKP;
-    private static X509Certificate _reciCert;
-
-    private static KeyPair         _origEcKP;
-    private static KeyPair         _reciEcKP;
-    private static X509Certificate _reciEcCert;
-
-    private static boolean         _initialised = false;
-
-    public boolean DEBUG = true;
-   
-    private static void init()
-        throws Exception
-    {
-        if (!_initialised)
-        {
-            _initialised = true;
-
-            _signDN   = "O=Bouncy Castle, C=AU";
-            _signKP   = CMSTestUtil.makeKeyPair();
-            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
-
-            _origDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
-            _origKP   = CMSTestUtil.makeKeyPair();
-            _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _signKP, _signDN);
-
-            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciKP   = CMSTestUtil.makeKeyPair();
-            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
-
-            _origEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcCert = CMSTestUtil.makeCertificate(_reciEcKP, _reciDN, _signKP, _signDN);
-        }
-    }
-
-    public void setUp()
-        throws Exception
-    {
-        init();
-    }
-
-    public AuthenticatedDataStreamTest(String name)
-    {
-        super(name);
-    }
-
-    public static void main(String args[])
-    {
-        junit.textui.TestRunner.run(AuthenticatedDataStreamTest.class);
-    }
-
-    public static Test suite()
-        throws Exception
-    {
-        init();
-
-        return new CMSTestSetup(new TestSuite(AuthenticatedDataStreamTest.class));
-    }
-
-    public void testKeyTransDESede()
-        throws Exception
-    {
-        tryKeyTrans(CMSAuthenticatedDataGenerator.DES_EDE3_CBC);
-    }
-
-    private void tryKeyTrans(String macAlg)
-        throws Exception
-    {
-        byte[]          data     = "Eric H. Echidna".getBytes();
-
-        CMSAuthenticatedDataStreamGenerator adGen = new CMSAuthenticatedDataStreamGenerator();
-        ByteArrayOutputStream               bOut = new ByteArrayOutputStream();
-
-        adGen.addKeyTransRecipient(_reciCert);
-
-        OutputStream aOut = adGen.open(bOut, macAlg, BC);
-
-        aOut.write(data);
-
-        aOut.close();
-
-        CMSAuthenticatedDataParser ad = new CMSAuthenticatedDataParser(bOut.toByteArray());
-
-        RecipientInformationStore recipients = ad.getRecipientInfos();
-
-        assertEquals(ad.getMacAlgOID(), macAlg);
-
-        Collection c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator it = c.iterator();
-
-        while (it.hasNext())
-        {
-            RecipientInformation recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertTrue(Arrays.equals(data, recData));
-            assertTrue(Arrays.equals(ad.getMac(), recipient.getMac()));
-        }
-    }
-}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/AuthenticatedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/AuthenticatedDataTest.java
deleted file mode 100644
index 454b369..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/AuthenticatedDataTest.java
+++ /dev/null
@@ -1,308 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.cert.X509Certificate;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-
-import javax.crypto.SecretKey;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.cms.CMSAuthenticatedData;
-import org.bouncycastle.cms.CMSAuthenticatedDataGenerator;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSPBEKey;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.PKCS5Scheme2PBEKey;
-import org.bouncycastle.cms.PasswordRecipientInformation;
-import org.bouncycastle.cms.RecipientInformation;
-import org.bouncycastle.cms.RecipientInformationStore;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Hex;
-
-public class AuthenticatedDataTest
-    extends TestCase
-{
-    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
-
-    private static String          _signDN;
-    private static KeyPair _signKP;
-    private static X509Certificate _signCert;
-
-    private static String          _origDN;
-    private static KeyPair         _origKP;
-    private static X509Certificate _origCert;
-
-    private static String          _reciDN;
-    private static KeyPair         _reciKP;
-    private static X509Certificate _reciCert;
-
-    private static KeyPair         _origEcKP;
-    private static KeyPair         _reciEcKP;
-    private static X509Certificate _reciEcCert;
-
-    private static boolean         _initialised = false;
-
-    public boolean DEBUG = true;
-    
-    private static void init()
-        throws Exception
-    {
-        if (!_initialised)
-        {
-            _initialised = true;
-
-            _signDN   = "O=Bouncy Castle, C=AU";
-            _signKP   = CMSTestUtil.makeKeyPair();
-            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
-
-            _origDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
-            _origKP   = CMSTestUtil.makeKeyPair();
-            _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _signKP, _signDN);
-
-            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciKP   = CMSTestUtil.makeKeyPair();
-            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
-
-            _origEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcCert = CMSTestUtil.makeCertificate(_reciEcKP, _reciDN, _signKP, _signDN);
-        }
-    }
-
-    public void setUp()
-        throws Exception
-    {
-        init();
-    }
-    
-    public AuthenticatedDataTest(String name)
-    {
-        super(name);
-    }
-
-    public static void main(String args[])
-    {
-        junit.textui.TestRunner.run(AuthenticatedDataTest.class);
-    }
-
-    public static Test suite()
-        throws Exception
-    {
-        init();
-
-        return new CMSTestSetup(new TestSuite(AuthenticatedDataTest.class));
-    }
-
-    public void testKeyTransDESede()
-        throws Exception
-    {
-        tryKeyTrans(CMSAuthenticatedDataGenerator.DES_EDE3_CBC);
-    }
-
-    public void testKEKDESede()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeDesede192Key(), new DERObjectIdentifier("1.2.840.113549.1.9.16.3.6"));
-    }
-
-    public void testPasswordAES256()
-        throws Exception
-    {
-        passwordTest(CMSAuthenticatedDataGenerator.AES256_CBC);
-    }
-
-    public void testECKeyAgree()
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSAuthenticatedDataGenerator adGen = new CMSAuthenticatedDataGenerator();
-
-        adGen.addKeyAgreementRecipient(CMSAuthenticatedDataGenerator.ECDH_SHA1KDF, _origEcKP.getPrivate(), _origEcKP.getPublic(), _reciEcCert, CMSAuthenticatedDataGenerator.AES128_WRAP, BC);
-
-        CMSAuthenticatedData ad = adGen.generate(
-                              new CMSProcessableByteArray(data),
-                              CMSAuthenticatedDataGenerator.DES_EDE3_CBC, BC);
-
-        RecipientInformationStore  recipients = ad.getRecipientInfos();
-
-        assertEquals(ad.getMacAlgOID(),
-                CMSAuthenticatedDataGenerator.DES_EDE3_CBC);
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(_reciEcKP.getPrivate(), BC);
-            assertTrue(Arrays.equals(data, recData));
-            assertTrue(Arrays.equals(ad.getMac(), recipient.getMac()));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testEncoding()
-        throws Exception
-    {
-        byte[]          data     = "Eric H. Echidna".getBytes();
-
-        CMSAuthenticatedDataGenerator adGen = new CMSAuthenticatedDataGenerator();
-
-        adGen.addKeyTransRecipient(_reciCert);
-
-        CMSAuthenticatedData ad = adGen.generate(
-                                new CMSProcessableByteArray(data),
-                                CMSAuthenticatedDataGenerator.DES_EDE3_CBC, BC);
-
-        ad = new CMSAuthenticatedData(ad.getEncoded());
-        
-        RecipientInformationStore recipients = ad.getRecipientInfos();
-
-        assertEquals(CMSAuthenticatedDataGenerator.DES_EDE3_CBC, ad.getMacAlgOID());
-
-        Collection c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator it = c.iterator();
-
-        while (it.hasNext())
-        {
-            RecipientInformation recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertTrue(Arrays.equals(data, recData));
-            assertTrue(Arrays.equals(ad.getMac(), recipient.getMac()));
-        }
-    }
-
-    private void tryKeyTrans(String macAlg)
-        throws Exception
-    {
-        byte[]          data     = "Eric H. Echidna".getBytes();
-
-        CMSAuthenticatedDataGenerator adGen = new CMSAuthenticatedDataGenerator();
-
-        adGen.addKeyTransRecipient(_reciCert);
-
-        CMSAuthenticatedData ad = adGen.generate(
-                                new CMSProcessableByteArray(data),
-                                macAlg, BC);
-
-        RecipientInformationStore recipients = ad.getRecipientInfos();
-
-        assertEquals(ad.getMacAlgOID(), macAlg);
-
-        Collection c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator it = c.iterator();
-
-        while (it.hasNext())
-        {
-            RecipientInformation recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertTrue(Arrays.equals(data, recData));
-            assertTrue(Arrays.equals(ad.getMac(), recipient.getMac()));
-        }
-    }
-
-    private void tryKekAlgorithm(SecretKey kek, DERObjectIdentifier algOid)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        byte[]          data     = "Eric H. Echidna".getBytes();
-
-        CMSAuthenticatedDataGenerator adGen = new CMSAuthenticatedDataGenerator();
-
-        byte[]  kekId = new byte[] { 1, 2, 3, 4, 5 };
-
-        adGen.addKEKRecipient(kek, kekId);
-
-        CMSAuthenticatedData ad = adGen.generate(
-                                new CMSProcessableByteArray(data),
-                                CMSAuthenticatedDataGenerator.DES_EDE3_CBC, BC);
-
-        RecipientInformationStore recipients = ad.getRecipientInfos();
-
-        Collection c = recipients.getRecipients();
-        Iterator it = c.iterator();
-
-        assertEquals(ad.getMacAlgOID(), CMSAuthenticatedDataGenerator.DES_EDE3_CBC);
-
-        if (it.hasNext())
-        {
-            RecipientInformation recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), algOid.getId());
-
-            byte[] recData = recipient.getContent(kek, BC);
-
-            assertTrue(Arrays.equals(data, recData));
-            assertTrue(Arrays.equals(ad.getMac(), recipient.getMac()));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    private void passwordTest(String algorithm)
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSAuthenticatedDataGenerator adGen = new CMSAuthenticatedDataGenerator();
-
-        adGen.addPasswordRecipient(new PKCS5Scheme2PBEKey("password".toCharArray(), new byte[20], 5), algorithm);
-
-        CMSAuthenticatedData ad = adGen.generate(
-                              new CMSProcessableByteArray(data),
-                              CMSAuthenticatedDataGenerator.DES_EDE3_CBC, BC);
-
-        RecipientInformationStore  recipients = ad.getRecipientInfos();
-
-        assertEquals(ad.getMacAlgOID(),
-                                   CMSAuthenticatedDataGenerator.DES_EDE3_CBC);
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            PasswordRecipientInformation recipient = (PasswordRecipientInformation)it.next();
-
-            CMSPBEKey key = new PKCS5Scheme2PBEKey("password".toCharArray(),
-                recipient.getKeyDerivationAlgParameters(BC));
-
-            byte[] recData = recipient.getContent(key, BC);
-
-            assertTrue(Arrays.equals(data, recData));
-            assertTrue(Arrays.equals(ad.getMac(), recipient.getMac()));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/BcSignedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/BcSignedDataTest.java
index 299f68d..1c4ccc0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/BcSignedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/BcSignedDataTest.java
@@ -61,6 +61,7 @@
 import org.bouncycastle.crypto.util.PrivateKeyFactory;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.BufferingContentSigner;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
@@ -572,6 +573,46 @@
         verifySignatures(s, null);
     }
 
+    public void testDetachedVerificationWithBufferingContentSigner()
+        throws Exception
+    {
+        byte[]              data = "Hello World!".getBytes();
+        List certList = new ArrayList();
+        CMSTypedData        msg = new CMSProcessableByteArray(data);
+
+        certList.add(_origCert);
+        certList.add(_signCert);
+
+        Store           certs = new JcaCertStore(certList);
+
+        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
+
+        DigestCalculatorProvider digProvider = new JcaDigestCalculatorProviderBuilder().setProvider(BC).build();
+        JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(digProvider);
+        ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA1withRSA").setProvider(BC).build(_origKP.getPrivate());
+        ContentSigner md5Signer = new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(_origKP.getPrivate());
+
+        gen.addSignerInfoGenerator(signerInfoGeneratorBuilder.build(new BufferingContentSigner(sha1Signer), _origCert));
+        gen.addSignerInfoGenerator(signerInfoGeneratorBuilder.build(new BufferingContentSigner(md5Signer), _origCert));
+
+        gen.addCertificates(certs);
+
+        CMSSignedData s = gen.generate(msg);
+
+        MessageDigest sha1 = MessageDigest.getInstance("SHA1", BC);
+        MessageDigest md5 = MessageDigest.getInstance("MD5", BC);
+        Map hashes = new HashMap();
+        byte[] sha1Hash = sha1.digest(data);
+        byte[] md5Hash = md5.digest(data);
+
+        hashes.put(CMSAlgorithm.SHA1, sha1Hash);
+        hashes.put(CMSAlgorithm.MD5, md5Hash);
+
+        s = new CMSSignedData(hashes, s.getEncoded());
+
+        verifySignatures(s, null);
+    }
+
     public void testSHA1AndMD5WithRSAEncapsulatedRepeated()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/CMSTestUtil.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/CMSTestUtil.java
index 4eb9841..3a1517a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/CMSTestUtil.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/CMSTestUtil.java
@@ -20,32 +20,38 @@
 import javax.crypto.SecretKey;
 
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
 import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
 import org.bouncycastle.asn1.x509.BasicConstraints;
 import org.bouncycastle.asn1.x509.CRLReason;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x509.X509Extension;
-import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509ExtensionUtils;
+import org.bouncycastle.cert.X509v1CertificateBuilder;
 import org.bouncycastle.cert.X509v2CRLBuilder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509CRLConverter;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
+import org.bouncycastle.cert.jcajce.JcaX509v1CertificateBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
 import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.spec.GOST3410ParameterSpec;
+import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.x509.X509AttributeCertificate;
-import org.bouncycastle.x509.X509StreamParser;
-import org.bouncycastle.x509.X509V1CertificateGenerator;
-import org.bouncycastle.x509.X509V3CertificateGenerator;
-import org.bouncycastle.x509.extension.AuthorityKeyIdentifierStructure;
 
 public class CMSTestUtil
 {
     public static SecureRandom     rand;
     public static KeyPairGenerator kpg;
+
     public static KeyPairGenerator gostKpg;
     public static KeyPairGenerator dsaKpg;
     public static KeyPairGenerator ecGostKpg;
@@ -114,7 +120,10 @@
 
             kpg  = KeyPairGenerator.getInstance("RSA", "BC");
             kpg.initialize(1024, rand);
-            
+
+            kpg  = KeyPairGenerator.getInstance("RSA", "BC");
+            kpg.initialize(1024, rand);
+
             gostKpg  = KeyPairGenerator.getInstance("GOST3410", "BC");
             GOST3410ParameterSpec gost3410P = new GOST3410ParameterSpec(CryptoProObjectIdentifiers.gostR3410_94_CryptoPro_A.getId());
             
@@ -189,14 +198,10 @@
         return buf.toString();
     }
 
-    public static X509AttributeCertificate getAttributeCertificate()
+    public static X509AttributeCertificateHolder getAttributeCertificate()
         throws Exception
     {
-        X509StreamParser parser = X509StreamParser.getInstance("AttributeCertificate", "BC");
-
-        parser.init(CMSTestUtil.attrCert);
-
-        return (X509AttributeCertificate)parser.read();
+        return  new X509AttributeCertificateHolder(CMSTestUtil.attrCert);
     }
 
     public static KeyPair makeKeyPair()
@@ -273,60 +278,44 @@
 
     public static X509Certificate makeCertificate(KeyPair _subKP,
             String _subDN, KeyPair _issKP, String _issDN)
-            throws GeneralSecurityException, IOException
+        throws GeneralSecurityException, IOException, OperatorCreationException
     {
-
         return makeCertificate(_subKP, _subDN, _issKP, _issDN, false);
     }
 
+    public static X509Certificate makeOaepCertificate(KeyPair _subKP,
+            String _subDN, KeyPair _issKP, String _issDN)
+        throws GeneralSecurityException, IOException, OperatorCreationException
+    {
+        return makeOaepCertificate(_subKP, _subDN, _issKP, _issDN, false);
+    }
+
     public static X509Certificate makeCACertificate(KeyPair _subKP,
             String _subDN, KeyPair _issKP, String _issDN)
-            throws GeneralSecurityException, IOException
+        throws GeneralSecurityException, IOException, OperatorCreationException
     {
-
         return makeCertificate(_subKP, _subDN, _issKP, _issDN, true);
     }
 
     public static X509Certificate makeV1Certificate(KeyPair subKP, String _subDN, KeyPair issKP, String _issDN)
-        throws GeneralSecurityException, IOException
+        throws GeneralSecurityException, IOException, OperatorCreationException
     {
 
         PublicKey  subPub  = subKP.getPublic();
         PrivateKey issPriv = issKP.getPrivate();
         PublicKey  issPub  = issKP.getPublic();
 
-        X509V1CertificateGenerator v1CertGen = new X509V1CertificateGenerator();
+        X509v1CertificateBuilder v1CertGen = new JcaX509v1CertificateBuilder(
+            new X500Name(_issDN),
+            allocateSerialNumber(),
+            new Date(System.currentTimeMillis()),
+            new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
+            new X500Name(_subDN),
+            subPub);
 
-        v1CertGen.reset();
-        v1CertGen.setSerialNumber(allocateSerialNumber());
-        v1CertGen.setIssuerDN(new X509Name(_issDN));
-        v1CertGen.setNotBefore(new Date(System.currentTimeMillis()));
-        v1CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)));
-        v1CertGen.setSubjectDN(new X509Name(_subDN));
-        v1CertGen.setPublicKey(subPub);
+        JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
 
-        if (issPub instanceof RSAPublicKey)
-        {
-            v1CertGen.setSignatureAlgorithm("SHA1WithRSA");
-        }
-        else if (issPub.getAlgorithm().equals("DSA"))
-        {
-            v1CertGen.setSignatureAlgorithm("SHA1withDSA");
-        }
-        else if (issPub.getAlgorithm().equals("ECDSA"))
-        {
-            v1CertGen.setSignatureAlgorithm("SHA1withECDSA");
-        }
-        else if (issPub.getAlgorithm().equals("ECGOST3410"))
-        {
-            v1CertGen.setSignatureAlgorithm("GOST3411withECGOST3410");
-        }
-        else
-        {
-            v1CertGen.setSignatureAlgorithm("GOST3411WithGOST3410");
-        }
-
-        X509Certificate _cert = v1CertGen.generate(issPriv);
+        X509Certificate _cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(v1CertGen.build(contentSignerBuilder.build(issPriv)));
 
         _cert.checkValidity(new Date());
         _cert.verify(issPub);
@@ -335,78 +324,128 @@
     }
 
     public static X509Certificate makeCertificate(KeyPair subKP, String _subDN, KeyPair issKP, String _issDN, boolean _ca)
-        throws GeneralSecurityException, IOException
+        throws GeneralSecurityException, IOException, OperatorCreationException
     {
 
         PublicKey  subPub  = subKP.getPublic();
         PrivateKey issPriv = issKP.getPrivate();
         PublicKey  issPub  = issKP.getPublic();
         
-        X509V3CertificateGenerator v3CertGen = new X509V3CertificateGenerator();
-        
-        v3CertGen.reset();
-        v3CertGen.setSerialNumber(allocateSerialNumber());
-        v3CertGen.setIssuerDN(new X509Name(_issDN));
-        v3CertGen.setNotBefore(new Date(System.currentTimeMillis()));
-        v3CertGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)));
-        v3CertGen.setSubjectDN(new X509Name(_subDN));
-        v3CertGen.setPublicKey(subPub);
-        
-        if (issPub instanceof RSAPublicKey)
-        {
-            v3CertGen.setSignatureAlgorithm("SHA1WithRSA");
-        }
-        else if (issPub.getAlgorithm().equals("DSA"))
-        {
-            v3CertGen.setSignatureAlgorithm("SHA1withDSA");
-        }
-        else if (issPub.getAlgorithm().equals("ECDSA"))
-        {
-            v3CertGen.setSignatureAlgorithm("SHA1withECDSA");
-        }
-        else if (issPub.getAlgorithm().equals("ECGOST3410"))
-        {
-            v3CertGen.setSignatureAlgorithm("GOST3411withECGOST3410");
-        }
-        else
-        {
-            v3CertGen.setSignatureAlgorithm("GOST3411WithGOST3410");
-        }
+        X509v3CertificateBuilder v3CertGen = new JcaX509v3CertificateBuilder(
+            new X500Name(_issDN),
+            allocateSerialNumber(),
+            new Date(System.currentTimeMillis()),
+            new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
+            new X500Name(_subDN),
+            subPub);
+
+        JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
 
         v3CertGen.addExtension(
-            X509Extension.subjectKeyIdentifier,
+            Extension.subjectKeyIdentifier,
             false,
             createSubjectKeyId(subPub));
 
         v3CertGen.addExtension(
-            X509Extension.authorityKeyIdentifier,
+            Extension.authorityKeyIdentifier,
             false,
             createAuthorityKeyId(issPub));
 
         v3CertGen.addExtension(
-            X509Extension.basicConstraints,
+            Extension.basicConstraints,
             false,
             new BasicConstraints(_ca));
 
-        X509Certificate _cert = v3CertGen.generate(issPriv);
+        X509Certificate _cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
 
         _cert.checkValidity(new Date());
         _cert.verify(issPub);
 
         return _cert;
     }
-    
+
+    public static X509Certificate makeOaepCertificate(KeyPair subKP, String _subDN, KeyPair issKP, String _issDN, boolean _ca)
+        throws GeneralSecurityException, IOException, OperatorCreationException
+    {
+
+        SubjectPublicKeyInfo  subPub  = SubjectPublicKeyInfo.getInstance(subKP.getPublic().getEncoded());
+        PrivateKey issPriv = issKP.getPrivate();
+        PublicKey  issPub  = issKP.getPublic();
+
+        X509v3CertificateBuilder v3CertGen = new X509v3CertificateBuilder(
+            new X500Name(_issDN),
+            allocateSerialNumber(),
+            new Date(System.currentTimeMillis()),
+            new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 100)),
+            new X500Name(_subDN),
+            new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, new RSAESOAEPparams()), subPub.parsePublicKey()));
+
+        JcaContentSignerBuilder contentSignerBuilder = makeContentSignerBuilder(issPub);
+
+        v3CertGen.addExtension(
+            Extension.subjectKeyIdentifier,
+            false,
+            createSubjectKeyId(subPub));
+
+        v3CertGen.addExtension(
+            Extension.authorityKeyIdentifier,
+            false,
+            createAuthorityKeyId(issPub));
+
+        v3CertGen.addExtension(
+            Extension.basicConstraints,
+            false,
+            new BasicConstraints(_ca));
+
+        X509Certificate _cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(v3CertGen.build(contentSignerBuilder.build(issPriv)));
+
+        _cert.checkValidity(new Date());
+        _cert.verify(issPub);
+
+        return _cert;
+    }
+
+    private static JcaContentSignerBuilder makeContentSignerBuilder(PublicKey issPub)
+    {
+        JcaContentSignerBuilder contentSignerBuilder;
+        if (issPub instanceof RSAPublicKey)
+        {
+            contentSignerBuilder = new JcaContentSignerBuilder("SHA1WithRSA");
+        }
+        else if (issPub.getAlgorithm().equals("DSA"))
+        {
+            contentSignerBuilder = new JcaContentSignerBuilder("SHA1withDSA");
+        }
+        else if (issPub.getAlgorithm().equals("ECDSA"))
+        {
+            contentSignerBuilder = new JcaContentSignerBuilder("SHA1withECDSA");
+        }
+        else if (issPub.getAlgorithm().equals("ECGOST3410"))
+        {
+            contentSignerBuilder = new JcaContentSignerBuilder("GOST3411withECGOST3410");
+        }
+        else
+        {
+            contentSignerBuilder = new JcaContentSignerBuilder("GOST3411WithGOST3410");
+        }
+
+        contentSignerBuilder.setProvider(BouncyCastleProvider.PROVIDER_NAME);
+
+        return contentSignerBuilder;
+    }
+
     public static X509CRL makeCrl(KeyPair pair)
         throws Exception
     {
         Date                 now = new Date();
         X509v2CRLBuilder crlGen = new X509v2CRLBuilder(new X500Name("CN=Test CA"), now);
+        JcaX509ExtensionUtils extensionUtils = new JcaX509ExtensionUtils();
 
         crlGen.setNextUpdate(new Date(now.getTime() + 100000));
 
         crlGen.addCRLEntry(BigInteger.ONE, now, CRLReason.privilegeWithdrawn);
 
-        crlGen.addExtension(X509Extension.authorityKeyIdentifier, false, new AuthorityKeyIdentifierStructure(pair.getPublic()));
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, extensionUtils.createAuthorityKeyIdentifier(pair.getPublic()));
 
         return new JcaX509CRLConverter().setProvider("BC").getCRL(crlGen.build(new JcaContentSignerBuilder("SHA256WithRSAEncryption").setProvider("BC").build(pair.getPrivate())));
     }
@@ -427,6 +466,13 @@
     }
 
     static SubjectKeyIdentifier createSubjectKeyId(
+        SubjectPublicKeyInfo _pubKey)
+        throws IOException
+    {
+        return extUtils.createSubjectKeyIdentifier(_pubKey);
+    }
+
+    static SubjectKeyIdentifier createSubjectKeyId(
         PublicKey _pubKey)
         throws IOException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/CompressedDataStreamTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/CompressedDataStreamTest.java
deleted file mode 100644
index f9e5d62..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/CompressedDataStreamTest.java
+++ /dev/null
@@ -1,126 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.util.Arrays;
-import java.util.Random;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-
-import org.bouncycastle.cms.CMSCompressedDataParser;
-import org.bouncycastle.cms.CMSCompressedDataStreamGenerator;
-import org.bouncycastle.util.encoders.Base64;
-
-public class CompressedDataStreamTest
-    extends TestCase
-{
-    public CompressedDataStreamTest(String name) 
-    {
-        super(name);
-    }
-
-    public void testWorkingData()
-        throws Exception
-    {
-        byte[]  compData = Base64.decode(
-                  "MIAGCyqGSIb3DQEJEAEJoIAwgAIBADANBgsqhkiG9w0BCRADCDCABgkqhkiG9w0BBwGggCSABIIC"
-                + "Hnic7ZRdb9owFIbvK/k/5PqVYPFXGK12YYyboVFASSp1vQtZGiLRACZE49/XHoUW7S/0tXP8Efux"
-                + "fU5ivWnasml72XFb3gb5druui7ytN803M570nii7C5r8tfwR281hy/p/KSM3+jzH5s3+pbQ90xSb"
-                + "P3VT3QbLusnt8WPIuN5vN/vaA2+DulnXTXkXvNTr8j8ouZmkCmGI/UW+ZS/C8zP0bz2dz0zwLt+1"
-                + "UEk2M8mlaxjRMByAhZTj0RGYg4TvogiRASROsZgjpVcJCb1KV6QzQeDJ1XkoQ5Jm+C5PbOHZZGRi"
-                + "v+ORAcshOGeCcdFJyfgFxdtCdEcmOrbinc/+BBMzRThEYpwl+jEBpciSGWQkI0TSlREmD/eOHb2D"
-                + "SGLuESm/iKUFt1y4XHBO2a5oq0IKJKWLS9kUZTA7vC5LSxYmgVL46SIWxIfWBQd6AdrnjLmH94UT"
-                + "vGxVibLqRCtIpp4g2qpdtqK1LiOeolpVK5wVQ5P7+QjZAlrh0cePYTx/gNZuB9Vhndtgujl9T/tg"
-                + "W9ogK+3rnmg3YWygnTuF5GDS+Q/jIVLnCcYZFc6Kk/+c80wKwZjwdZIqDYWRH68MuBQSXLgXYXj2"
-                + "3CAaYOBNJMliTl0X7eV5DnoKIFSKYdj3cRpD/cK/JWTHJRe76MUXnfBW8m7Hd5zhQ4ri2NrVF/WL"
-                + "+kV1/3AGSlJ32bFPd2BsQD8uSzIx6lObkjdz95c0AAAAAAAAAAAAAAAA");
-
-        byte[]  uncompData = Base64.decode(
-                  "Q29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9FREktWDEyOyBuYW1lPUdyb3VwMi54MTINCkNvbnRl"
-                + "bnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJpbmFyeQ0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5l"
-                + "OyBmaWxlbmFtZT1Hcm91cDIueDEyDQoNCklTQSowMCpzc3Nzc3Nzc3NzKjAwKnJycnJycnJycnIqW"
-                + "loqQ1lDTE9ORSAgICAgICAgKlpaKlBBUlRORVIgICAgICAgICo5NjEwMDcqMjAxMypVKjAwMjAwKj"
-                + "AwMDAwMDAwMSowKlQqKg1HUypQTypTMVMxUzFTMVMxUzFTMVMqUjFSMVIxUjFSMVIxUjFSKjk2MTA"
-                + "wNyoyMDEzKjAwMDAwMDAwNCpYKjAwMzA1MA1TVCo4NTAqMDAwMDQwMDAxDUJFRyowMCpCRSoyYSo0"
-                + "MzMyNDIzNHY1NTIzKjk2MTAwNyoyM3RjNHZ5MjR2MmgzdmgzdmgqWloqSUVMKjA5KlJFKjA5DUNVU"
-                + "ioxMSpUUk4qNTY1Nio2NSo1NjYqSU1GKjAwNio5NjEwMDcNUkVGKjZBKjQzM3IxYzNyMzRyMzRjMz"
-                + "MxMnFjdGdjNTQqUmVmZXJlbmNlIE51bWJlcg1QRVIqQUEqSGFucyBHdXR0ZW4qQ1AqMS4zMjIuMzI"
-                + "zLjQ0NDQqKioqKnJnZzRlZ3Y0dDQNVEFYKjR0Z3RidDR0cjR0cipHTCpnaGdoKioqKioqKioqRypD"
-                + "DUZPQipUUCpDQSpVU0EqMDIqRE9NKkNDKlJlZ3VsYXIgTG9jYXRpb25zIHBlciBUZXJtcw1DVFAqR"
-                + "EUqQzA0KjQ1MyoyNTAwMCpEOSpTRUwqMjMyMTQqMjM0MzI0MjM0MjMqRVMqNDIyNDM0MjMNU0FDKk"
-                + "EqQjAwMCpBRSozNTQ1KjM0NDIzMDANQ1VSKjExKjc2Nyo3NzY3KjY1DVBPMSoxMTEtYWFhKjEwMDA"
-                + "wMDAqQVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRxNmYzNTM0djQzNTM0NTN2cTNxMzIqKioqKioq"
-                + "KioqKkExKnl0cmgNUE8xKjExMS1hYWEqMTAwMDAwMCpBUyo5MC4wMCpCRCpBSyoyMzQyMzV2MzUzN"
-                + "HE2ZjM1MzR2NDM1MzQ1M3ZxM3EzMioqKioqKioqKioqQTEqeXRyaA1QTzEqMTExLWFhYSoxMDAwMD"
-                + "AwKkFTKjkwLjAwKkJEKkFLKjIzNDIzNXYzNTM0cTZmMzUzNHY0MzUzNDUzdnEzcTMyKioqKioqKio"
-                + "qKipBMSp5dHJoDVBPMSoxMTEtYWFhKjEwMDAwMDAqQVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRx"
-                + "NmYzNTM0djQzNTM0NTN2cTNxMzIqKioqKioqKioqKkExKnl0cmgNUE8xKjExMS1hYWEqMTAwMDAwM"
-                + "CpBUyo5MC4wMCpCRCpBSyoyMzQyMzV2MzUzNHE2ZjM1MzR2NDM1MzQ1M3ZxM3EzMioqKioqKioqKi"
-                + "oqQTEqeXRyaA1QTzEqMTExLWFhYSoxMDAwMDAwKkFTKjkwLjAwKkJEKkFLKjIzNDIzNXYzNTM0cTZ"
-                + "mMzUzNHY0MzUzNDUzdnEzcTMyKioqKioqKioqKipBMSp5dHJoDVBPMSoxMTEtYWFhKjEwMDAwMDAq"
-                + "QVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRxNmYzNTM0djQzNTM0NTN2cTNxMzIqKioqKioqKioqK"
-                + "kExKnl0cmgNUE8xKjExMS1hYWEqMTAwMDAwMCpBUyo5MC4wMCpCRCpBSyoyMzQyMzV2MzUzNHE2Zj"
-                + "M1MzR2NDM1MzQ1M3ZxM3EzMioqKioqKioqKioqQTEqeXRyaA1QTzEqMTExLWFhYSoxMDAwMDAwKkF"
-                + "TKjkwLjAwKkJEKkFLKjIzNDIzNXYzNTM0cTZmMzUzNHY0MzUzNDUzdnEzcTMyKioqKioqKioqKipB"
-                + "MSp5dHJoDVBPMSoxMTEtYWFhKjEwMDAwMDAqQVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRxNmYzN"
-                + "TM0djQzNTM0NTN2cTNxMzIqKioqKioqKioqKkExKnl0cmgNQ1RUKjENU0UqMjIqMDAwMDQwMDAxDU"
-                + "dFKjEqMDAwMDAwMDA0DUlFQSoxKjAwMDAwMDAwMQ0=");
-
-        CMSCompressedDataParser ed = new CMSCompressedDataParser(compData);
-
-        assertEquals(true, Arrays.equals(uncompData, CMSTestUtil.streamToByteArray(ed.getContent().getContentStream())));
-    }
-
-    public void testEach()
-        throws Exception
-    {
-        byte[]  testData = "Hello world!".getBytes();
-
-        CMSCompressedDataStreamGenerator gen = new CMSCompressedDataStreamGenerator();
-        ByteArrayOutputStream            bOut = new ByteArrayOutputStream();
-        
-        OutputStream cOut = gen.open(bOut, CMSCompressedDataStreamGenerator.ZLIB);
-
-        cOut.write(testData);
-        
-        cOut.close();
-
-        CMSCompressedDataParser ed = new CMSCompressedDataParser(bOut.toByteArray());
-        
-        assertEquals(true, Arrays.equals(testData, CMSTestUtil.streamToByteArray(ed.getContent().getContentStream())));
-    }
-    
-    public void test1000()
-        throws Exception
-    {
-        byte[]  testData = new byte[10000];
-        Random  rand = new Random();
-        
-        rand.setSeed(0);
-
-        for (int i = 0; i != 10; i++)
-        {   
-            CMSCompressedDataStreamGenerator gen = new CMSCompressedDataStreamGenerator();
-            ByteArrayOutputStream            bOut = new ByteArrayOutputStream();
-            
-            OutputStream cOut = gen.open(bOut, CMSCompressedDataStreamGenerator.ZLIB);
-
-            rand.nextBytes(testData);
-            
-            cOut.write(testData);
-            
-            cOut.close();
-
-            CMSCompressedDataParser ed = new CMSCompressedDataParser(bOut.toByteArray());
-            
-            assertEquals(true, Arrays.equals(testData, CMSTestUtil.streamToByteArray(ed.getContent().getContentStream())));
-        }
-    }
-    
-    public static Test suite()
-    {
-        return new TestSuite(CompressedDataStreamTest.class);
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/CompressedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/CompressedDataTest.java
deleted file mode 100644
index 6fd06b3..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/CompressedDataTest.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.util.Arrays;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.cms.CMSCompressedData;
-import org.bouncycastle.cms.CMSCompressedDataGenerator;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.io.StreamOverflowException;
-
-public class CompressedDataTest
-    extends TestCase
-{
-    private static final byte[] TEST_DATA = "Hello world!".getBytes();
-
-    /*
-     *
-     *  INFRASTRUCTURE
-     *
-     */
-
-    public CompressedDataTest(String name)
-    {
-        super(name);
-    }
-
-    public static void main(String args[])
-    {
-        junit.textui.TestRunner.run(CompressedDataTest.class);
-    }
-
-    public static Test suite()
-    {
-        return new CMSTestSetup(new TestSuite(CompressedDataTest.class));
-    }
-
-    public void setUp()
-    {
-
-    }
-
-    public void tearDown()
-    {
-
-    }
-
-    public void testWorkingData()
-        throws Exception
-    {
-        byte[] compData = Base64
-                .decode("MIAGCyqGSIb3DQEJEAEJoIAwgAIBADANBgsqhkiG9w0BCRADCDCABgkqhkiG9w0BBwGggCSABIIC"
-                        + "Hnic7ZRdb9owFIbvK/k/5PqVYPFXGK12YYyboVFASSp1vQtZGiLRACZE49/XHoUW7S/0tXP8Efux"
-                        + "fU5ivWnasml72XFb3gb5druui7ytN803M570nii7C5r8tfwR281hy/p/KSM3+jzH5s3+pbQ90xSb"
-                        + "P3VT3QbLusnt8WPIuN5vN/vaA2+DulnXTXkXvNTr8j8ouZmkCmGI/UW+ZS/C8zP0bz2dz0zwLt+1"
-                        + "UEk2M8mlaxjRMByAhZTj0RGYg4TvogiRASROsZgjpVcJCb1KV6QzQeDJ1XkoQ5Jm+C5PbOHZZGRi"
-                        + "v+ORAcshOGeCcdFJyfgFxdtCdEcmOrbinc/+BBMzRThEYpwl+jEBpciSGWQkI0TSlREmD/eOHb2D"
-                        + "SGLuESm/iKUFt1y4XHBO2a5oq0IKJKWLS9kUZTA7vC5LSxYmgVL46SIWxIfWBQd6AdrnjLmH94UT"
-                        + "vGxVibLqRCtIpp4g2qpdtqK1LiOeolpVK5wVQ5P7+QjZAlrh0cePYTx/gNZuB9Vhndtgujl9T/tg"
-                        + "W9ogK+3rnmg3YWygnTuF5GDS+Q/jIVLnCcYZFc6Kk/+c80wKwZjwdZIqDYWRH68MuBQSXLgXYXj2"
-                        + "3CAaYOBNJMliTl0X7eV5DnoKIFSKYdj3cRpD/cK/JWTHJRe76MUXnfBW8m7Hd5zhQ4ri2NrVF/WL"
-                        + "+kV1/3AGSlJ32bFPd2BsQD8uSzIx6lObkjdz95c0AAAAAAAAAAAAAAAA");
-
-        byte[] uncompData = Base64
-                .decode("Q29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9FREktWDEyOyBuYW1lPUdyb3VwMi54MTINCkNvbnRl"
-                        + "bnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJpbmFyeQ0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5l"
-                        + "OyBmaWxlbmFtZT1Hcm91cDIueDEyDQoNCklTQSowMCpzc3Nzc3Nzc3NzKjAwKnJycnJycnJycnIqW"
-                        + "loqQ1lDTE9ORSAgICAgICAgKlpaKlBBUlRORVIgICAgICAgICo5NjEwMDcqMjAxMypVKjAwMjAwKj"
-                        + "AwMDAwMDAwMSowKlQqKg1HUypQTypTMVMxUzFTMVMxUzFTMVMqUjFSMVIxUjFSMVIxUjFSKjk2MTA"
-                        + "wNyoyMDEzKjAwMDAwMDAwNCpYKjAwMzA1MA1TVCo4NTAqMDAwMDQwMDAxDUJFRyowMCpCRSoyYSo0"
-                        + "MzMyNDIzNHY1NTIzKjk2MTAwNyoyM3RjNHZ5MjR2MmgzdmgzdmgqWloqSUVMKjA5KlJFKjA5DUNVU"
-                        + "ioxMSpUUk4qNTY1Nio2NSo1NjYqSU1GKjAwNio5NjEwMDcNUkVGKjZBKjQzM3IxYzNyMzRyMzRjMz"
-                        + "MxMnFjdGdjNTQqUmVmZXJlbmNlIE51bWJlcg1QRVIqQUEqSGFucyBHdXR0ZW4qQ1AqMS4zMjIuMzI"
-                        + "zLjQ0NDQqKioqKnJnZzRlZ3Y0dDQNVEFYKjR0Z3RidDR0cjR0cipHTCpnaGdoKioqKioqKioqRypD"
-                        + "DUZPQipUUCpDQSpVU0EqMDIqRE9NKkNDKlJlZ3VsYXIgTG9jYXRpb25zIHBlciBUZXJtcw1DVFAqR"
-                        + "EUqQzA0KjQ1MyoyNTAwMCpEOSpTRUwqMjMyMTQqMjM0MzI0MjM0MjMqRVMqNDIyNDM0MjMNU0FDKk"
-                        + "EqQjAwMCpBRSozNTQ1KjM0NDIzMDANQ1VSKjExKjc2Nyo3NzY3KjY1DVBPMSoxMTEtYWFhKjEwMDA"
-                        + "wMDAqQVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRxNmYzNTM0djQzNTM0NTN2cTNxMzIqKioqKioq"
-                        + "KioqKkExKnl0cmgNUE8xKjExMS1hYWEqMTAwMDAwMCpBUyo5MC4wMCpCRCpBSyoyMzQyMzV2MzUzN"
-                        + "HE2ZjM1MzR2NDM1MzQ1M3ZxM3EzMioqKioqKioqKioqQTEqeXRyaA1QTzEqMTExLWFhYSoxMDAwMD"
-                        + "AwKkFTKjkwLjAwKkJEKkFLKjIzNDIzNXYzNTM0cTZmMzUzNHY0MzUzNDUzdnEzcTMyKioqKioqKio"
-                        + "qKipBMSp5dHJoDVBPMSoxMTEtYWFhKjEwMDAwMDAqQVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRx"
-                        + "NmYzNTM0djQzNTM0NTN2cTNxMzIqKioqKioqKioqKkExKnl0cmgNUE8xKjExMS1hYWEqMTAwMDAwM"
-                        + "CpBUyo5MC4wMCpCRCpBSyoyMzQyMzV2MzUzNHE2ZjM1MzR2NDM1MzQ1M3ZxM3EzMioqKioqKioqKi"
-                        + "oqQTEqeXRyaA1QTzEqMTExLWFhYSoxMDAwMDAwKkFTKjkwLjAwKkJEKkFLKjIzNDIzNXYzNTM0cTZ"
-                        + "mMzUzNHY0MzUzNDUzdnEzcTMyKioqKioqKioqKipBMSp5dHJoDVBPMSoxMTEtYWFhKjEwMDAwMDAq"
-                        + "QVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRxNmYzNTM0djQzNTM0NTN2cTNxMzIqKioqKioqKioqK"
-                        + "kExKnl0cmgNUE8xKjExMS1hYWEqMTAwMDAwMCpBUyo5MC4wMCpCRCpBSyoyMzQyMzV2MzUzNHE2Zj"
-                        + "M1MzR2NDM1MzQ1M3ZxM3EzMioqKioqKioqKioqQTEqeXRyaA1QTzEqMTExLWFhYSoxMDAwMDAwKkF"
-                        + "TKjkwLjAwKkJEKkFLKjIzNDIzNXYzNTM0cTZmMzUzNHY0MzUzNDUzdnEzcTMyKioqKioqKioqKipB"
-                        + "MSp5dHJoDVBPMSoxMTEtYWFhKjEwMDAwMDAqQVMqOTAuMDAqQkQqQUsqMjM0MjM1djM1MzRxNmYzN"
-                        + "TM0djQzNTM0NTN2cTNxMzIqKioqKioqKioqKkExKnl0cmgNQ1RUKjENU0UqMjIqMDAwMDQwMDAxDUdFKjEqMDAwMDAwMDA0DUlFQSoxKjAwMDAwMDAwMQ0=");
-
-        CMSCompressedData ed = new CMSCompressedData(compData);
-
-        assertEquals(true, Arrays.equals(uncompData, ed.getContent()));
-    }
-
-    public void testEach()
-        throws Exception
-    {
-        CMSCompressedData cd = getStdData();
-
-        assertEquals(true, Arrays.equals(TEST_DATA, cd.getContent()));
-    }
-
-    public void testLimitUnder()
-        throws Exception
-    {
-        CMSCompressedData cd = getStdData();
-
-        try
-        {
-            cd.getContent(TEST_DATA.length / 2);
-        }
-        catch (CMSException e)
-        {
-            assertEquals(true, e.getCause() instanceof StreamOverflowException);
-        }
-    }
-
-    public void testLimitOver()
-        throws Exception
-    {
-        CMSCompressedData cd = getStdData();
-
-        assertEquals(true, Arrays.equals(TEST_DATA, cd.getContent(TEST_DATA.length * 2)));
-    }
-
-    public void testLimitEqual()
-        throws Exception
-    {
-        CMSCompressedData cd = getStdData();
-
-        assertEquals(true, Arrays.equals(TEST_DATA, cd.getContent(TEST_DATA.length)));
-    }
-
-    private CMSCompressedData getStdData()
-        throws CMSException
-    {
-        CMSProcessableByteArray testData = new CMSProcessableByteArray(TEST_DATA);
-        CMSCompressedDataGenerator gen = new CMSCompressedDataGenerator();
-
-        return gen.generate(testData,
-                CMSCompressedDataGenerator.ZLIB);
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/EnvelopedDataStreamTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/EnvelopedDataStreamTest.java
deleted file mode 100644
index 046db10..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/EnvelopedDataStreamTest.java
+++ /dev/null
@@ -1,631 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.Security;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-
-import javax.crypto.SecretKey;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.DEROutputStream;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
-import org.bouncycastle.cms.CMSEnvelopedDataParser;
-import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
-import org.bouncycastle.cms.CMSTypedStream;
-import org.bouncycastle.cms.KEKRecipientId;
-import org.bouncycastle.cms.RecipientId;
-import org.bouncycastle.cms.RecipientInformation;
-import org.bouncycastle.cms.RecipientInformationStore;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipientId;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.encoders.Hex;
-
-public class EnvelopedDataStreamTest
-    extends TestCase
-{
-    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
-
-    private static final int BUFFER_SIZE = 4000;
-    private static String          _signDN;
-    private static KeyPair         _signKP;
-    private static X509Certificate _signCert;
-
-    private static String          _origDN;
-    private static KeyPair         _origKP;
-    private static X509Certificate _origCert;
-
-    private static String          _reciDN;
-    private static KeyPair         _reciKP;
-    private static X509Certificate _reciCert;
-
-    private static KeyPair         _origEcKP;
-    private static KeyPair         _reciEcKP;
-    private static X509Certificate _reciEcCert;
-
-    private static boolean         _initialised = false;
-    
-    public EnvelopedDataStreamTest()
-    {
-    }
-
-    private static void init()
-        throws Exception
-    {
-        if (!_initialised)
-        {
-            _initialised = true;
-            
-            _signDN   = "O=Bouncy Castle, C=AU";
-            _signKP   = CMSTestUtil.makeKeyPair();  
-            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
-    
-            _origDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
-            _origKP   = CMSTestUtil.makeKeyPair();
-            _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _signKP, _signDN);
-    
-            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciKP   = CMSTestUtil.makeKeyPair();
-            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
-
-            _origEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcCert = CMSTestUtil.makeCertificate(_reciEcKP, _reciDN, _signKP, _signDN);
-        }
-    }
-    
-    public void setUp()
-        throws Exception
-    {
-        init();
-    }
-    
-    public void testWorkingData()
-        throws Exception
-    {
-        byte[]  keyData = Base64.decode(
-                  "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKrAz/SQKrcQ" +
-                  "nj9IxHIfKDbuXsMqUpI06s2gps6fp7RDNvtUDDMOciWGFhD45YSy8GO0mPx3" +
-                  "Nkc7vKBqX4TLcqLUz7kXGOHGOwiPZoNF+9jBMPNROe/B0My0PkWg9tuq+nxN" +
-                  "64oD47+JvDwrpNOS5wsYavXeAW8Anv9ZzHLU7KwZAgMBAAECgYA/fqdVt+5K" +
-                  "WKGfwr1Z+oAHvSf7xtchiw/tGtosZ24DOCNP3fcTXUHQ9kVqVkNyzt9ZFCT3" +
-                  "bJUAdBQ2SpfuV4DusVeQZVzcROKeA09nPkxBpTefWbSDQGhb+eZq9L8JDRSW" +
-                  "HyYqs+MBoUpLw7GKtZiJkZyY6CsYkAnQ+uYVWq/TIQJBAP5zafO4HUV/w4KD" +
-                  "VJi+ua+GYF1Sg1t/dYL1kXO9GP1p75YAmtm6LdnOCas7wj70/G1YlPGkOP0V" +
-                  "GFzeG5KAmAUCQQCryvKU9nwWA+kypcQT9Yr1P4vGS0APYoBThnZq7jEPc5Cm" +
-                  "ZI82yseSxSeea0+8KQbZ5mvh1p3qImDLEH/iNSQFAkAghS+tboKPN10NeSt+" +
-                  "uiGRRWNbiggv0YJ7Uldcq3ZeLQPp7/naiekCRUsHD4Qr97OrZf7jQ1HlRqTu" +
-                  "eZScjMLhAkBNUMZCQnhwFAyEzdPkQ7LpU1MdyEopYmRssuxijZao5JLqQAGw" +
-                  "YCzXokGFa7hz72b09F4DQurJL/WuDlvvu4jdAkEAxwT9lylvfSfEQw4/qQgZ" +
-                  "MFB26gqB6Gqs1pHIZCzdliKx5BO3VDeUGfXMI8yOkbXoWbYx5xPid/+N8R//" +
-                  "+sxLBw==");
-        
-        byte[] envData = Base64.decode(
-                  "MIAGCSqGSIb3DQEHA6CAMIACAQAxgcQwgcECAQAwKjAlMRYwFAYDVQQKEw1C" +
-                  "b3VuY3kgQ2FzdGxlMQswCQYDVQQGEwJBVQIBHjANBgkqhkiG9w0BAQEFAASB" +
-                  "gDmnaDZ0vDJNlaUSYyEXsgbaUH+itNTjCOgv77QTX2ImXj+kTctM19PQF2I1" +
-                  "0/NL0fjakvCgBTHKmk13a7jqB6cX3bysenHNrglHsgNGgeXQ7ggAq5fV/JQQ" +
-                  "T7rSxEtuwpbuHQnoVUZahOHVKy/a0uLr9iIh1A3y+yZTZaG505ZJMIAGCSqG" +
-                  "SIb3DQEHATAdBglghkgBZQMEAQIEENmkYNbDXiZxJWtq82qIRZKggAQgkOGr" +
-                  "1JcTsADStez1eY4+rO4DtyBIyUYQ3pilnbirfPkAAAAAAAAAAAAA");
-
-
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(envData);
-
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-
-        assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyData);
-        KeyFactory          keyFact = KeyFactory.getInstance("RSA", BC);
-        Key                 priKey = keyFact.generatePrivate(keySpec);
-        byte[]              data = Hex.decode("57616c6c6157616c6c6157617368696e67746f6e");
-
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            CMSTypedStream recData = recipient.getContentStream(priKey, BC);
-            
-            assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-        }
-    }
-    
-    private void verifyData(
-        ByteArrayOutputStream encodedStream,
-        String                expectedOid,
-        byte[]                expectedData)
-        throws Exception
-    {
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(encodedStream.toByteArray());
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-    
-        assertEquals(ep.getEncryptionAlgOID(), expectedOid);
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-        
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-    
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            CMSTypedStream recData = recipient.getContentStream(_reciKP.getPrivate(), BC);
-            
-            assertEquals(true, Arrays.equals(expectedData, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-        }
-    }
-    
-    public void testKeyTransAES128BufferedStream()
-        throws Exception
-    {
-        byte[] data = new byte[2000];
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            data[i] = (byte)(i & 0xff);
-        }
-        
-        //
-        // unbuffered
-        //
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        for (int i = 0; i != 2000; i++)
-        {
-            out.write(data[i]);
-        }
-        
-        out.close();
-        
-        verifyData(bOut, CMSEnvelopedDataGenerator.AES128_CBC, data);
-        
-        int unbufferedLength = bOut.toByteArray().length;
-        
-        //
-        // Using buffered output - should be == to unbuffered
-        //
-        edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        bOut = new ByteArrayOutputStream();
-        
-        out = edGen.open(bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        BufferedOutputStream bfOut = new BufferedOutputStream(out, 300);
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            bfOut.write(data[i]);
-        }
-        
-        bfOut.close();
-        
-        verifyData(bOut, CMSEnvelopedDataGenerator.AES128_CBC, data);
-
-        assertTrue(bOut.toByteArray().length == unbufferedLength);
-    }
-    
-    public void testKeyTransAES128Buffered()
-        throws Exception
-    {
-        byte[] data = new byte[2000];
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            data[i] = (byte)(i & 0xff);
-        }
-        
-        //
-        // unbuffered
-        //
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        for (int i = 0; i != 2000; i++)
-        {
-            out.write(data[i]);
-        }
-        
-        out.close();
-        
-        verifyData(bOut, CMSEnvelopedDataGenerator.AES128_CBC, data);
-        
-        int unbufferedLength = bOut.toByteArray().length;
-        
-        //
-        // buffered - less than default of 1000
-        //
-        edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.setBufferSize(300);
-        
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        bOut = new ByteArrayOutputStream();
-        
-        out = edGen.open(bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        for (int i = 0; i != 2000; i++)
-        {
-            out.write(data[i]);
-        }
-        
-        out.close();
-        
-        verifyData(bOut, CMSEnvelopedDataGenerator.AES128_CBC, data);
-
-        assertTrue(bOut.toByteArray().length > unbufferedLength);
-    }
-    
-    public void testKeyTransAES128Der()
-        throws Exception
-    {
-        byte[] data = new byte[2000];
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            data[i] = (byte)(i & 0xff);
-        }
-
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        for (int i = 0; i != 2000; i++)
-        {
-            out.write(data[i]);
-        }
-        
-        out.close();
-        
-        // convert to DER
-        ASN1InputStream aIn = new ASN1InputStream(bOut.toByteArray());
-        
-        bOut.reset();
-        
-        DEROutputStream dOut = new DEROutputStream(bOut);
-        
-        dOut.writeObject(aIn.readObject());
-  
-        verifyData(bOut, CMSEnvelopedDataGenerator.AES128_CBC, data);
-    }
-    
-    public void testKeyTransAES128Throughput()
-        throws Exception
-    {
-        byte[] data = new byte[40001];
-        
-        for (int i = 0; i != data.length; i++)
-        {
-            data[i] = (byte)(i & 0xff);
-        }
-        
-        //
-        // buffered
-        //
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.setBufferSize(BUFFER_SIZE);
-        
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        for (int i = 0; i != data.length; i++)
-        {
-            out.write(data[i]);
-        }
-        
-        out.close();
-        
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(bOut.toByteArray());
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-        Collection                 c = recipients.getRecipients();
-        Iterator                   it = c.iterator();
-        
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-    
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            CMSTypedStream recData = recipient.getContentStream(_reciKP.getPrivate(), BC);
-            
-            InputStream           dataStream = recData.getContentStream();
-            ByteArrayOutputStream dataOut = new ByteArrayOutputStream();
-            int                   len;
-            byte[]                buf = new byte[BUFFER_SIZE];
-            int                   count = 0;
-            
-            while (count != 10 && (len = dataStream.read(buf)) > 0)
-            {
-                assertEquals(buf.length, len);
-                
-                dataOut.write(buf);
-                count++;
-            }
-            
-            len = dataStream.read(buf);
-            dataOut.write(buf, 0, len);
-            
-            assertEquals(true, Arrays.equals(data, dataOut.toByteArray()));
-        }
-        else
-        {
-            fail("recipient not found.");
-        }
-    }
-    
-    public void testKeyTransAES128()
-        throws Exception
-    {
-        byte[]          data     = "WallaWallaWashington".getBytes();
-        
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        edGen.addKeyTransRecipient(_reciCert);
-    
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut, CMSEnvelopedDataGenerator.AES128_CBC, BC);
-    
-        out.write(data);
-        
-        out.close();
-        
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(bOut.toByteArray());
-    
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-    
-        assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-        
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-    
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            CMSTypedStream recData = recipient.getContentStream(_reciKP.getPrivate(), BC);
-            
-            assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-        }
-        
-        ep.close();
-    }
-    
-    public void testKeyTransCAST5SunJCE()
-        throws Exception
-    {
-        if (Security.getProvider("SunJCE") == null)
-        {
-            return;
-        }
-        
-        String version = System.getProperty("java.version");
-        if (version.startsWith("1.4") || version.startsWith("1.3"))
-        {
-            return;
-        }
-        
-        byte[]          data     = "WallaWallaWashington".getBytes();
-        
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut, CMSEnvelopedDataGenerator.CAST5_CBC, "SunJCE");
-
-        out.write(data);
-        
-        out.close();
-        
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(bOut.toByteArray());
-
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-
-        assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.CAST5_CBC);
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-        
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            CMSTypedStream recData = recipient.getContentStream(_reciKP.getPrivate(), "SunJCE");
-            
-            assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-        }
-        
-        ep.close();
-    }
-    
-    public void testAESKEK()
-        throws Exception
-    {
-        byte[]    data = "WallaWallaWashington".getBytes();
-        SecretKey kek  = CMSTestUtil.makeAES192Key();
-        
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-
-        byte[]  kekId = new byte[] { 1, 2, 3, 4, 5 };
-
-        edGen.addKEKRecipient(kek, kekId);
-
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut,
-                                CMSEnvelopedDataGenerator.DES_EDE3_CBC, BC);
-        out.write(data);
-        
-        out.close();
-         
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(bOut.toByteArray());
-        
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-
-        assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), "2.16.840.1.101.3.4.1.25");
-            
-            CMSTypedStream recData = recipient.getContentStream(kek, BC);
-            
-            assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-        }
-        
-        ep.close();
-    }
-    
-    public void testTwoAESKEK()
-        throws Exception
-    {
-        byte[]    data = "WallaWallaWashington".getBytes();
-        SecretKey kek1  = CMSTestUtil.makeAES192Key();
-        SecretKey kek2  = CMSTestUtil.makeAES192Key();
-
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-    
-        byte[]  kekId1 = new byte[] { 1, 2, 3, 4, 5 };
-        byte[]  kekId2 = new byte[] { 5, 4, 3, 2, 1 };
-    
-        edGen.addKEKRecipient(kek1, kekId1);
-        edGen.addKEKRecipient(kek2, kekId2);
-    
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-        
-        OutputStream out = edGen.open(
-                                bOut,
-                                CMSEnvelopedDataGenerator.DES_EDE3_CBC, BC);
-        out.write(data);
-        
-        out.close();
-         
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(bOut.toByteArray());
-        
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-    
-        assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-        
-        RecipientId                recSel = new KEKRecipientId(kekId2);
-        
-        RecipientInformation       recipient = recipients.get(recSel);
-        
-        assertEquals(recipient.getKeyEncryptionAlgOID(), "2.16.840.1.101.3.4.1.25");
-        
-        CMSTypedStream recData = recipient.getContentStream(kek2, BC);
-        
-        assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-
-        ep.close();
-    }
-
-    public void testECKeyAgree()
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedDataStreamGenerator edGen = new CMSEnvelopedDataStreamGenerator();
-
-        edGen.addKeyAgreementRecipient(CMSEnvelopedDataGenerator.ECDH_SHA1KDF, _origEcKP.getPrivate(), _origEcKP.getPublic(), _reciEcCert, CMSEnvelopedDataGenerator.AES128_WRAP, BC);
-
-        ByteArrayOutputStream  bOut = new ByteArrayOutputStream();
-
-        OutputStream out = edGen.open(
-                                bOut,
-                                CMSEnvelopedDataGenerator.AES128_CBC, BC);
-        out.write(data);
-
-        out.close();
-
-        CMSEnvelopedDataParser     ep = new CMSEnvelopedDataParser(bOut.toByteArray());
-
-        RecipientInformationStore  recipients = ep.getRecipientInfos();
-
-        assertEquals(ep.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
-
-        RecipientId                recSel = new JceKeyAgreeRecipientId(_reciEcCert);
-
-        RecipientInformation       recipient = recipients.get(recSel);
-
-        CMSTypedStream recData = recipient.getContentStream(_reciEcKP.getPrivate(), BC);
-
-        assertEquals(true, Arrays.equals(data, CMSTestUtil.streamToByteArray(recData.getContentStream())));
-
-        ep.close();
-    }
-
-    public void testOriginatorInfo()
-        throws Exception
-    {
-        CMSEnvelopedDataParser env = new CMSEnvelopedDataParser(CMSSampleMessages.originatorMessage);
-
-        env.getRecipientInfos();
-
-        assertEquals(CMSEnvelopedDataGenerator.DES_EDE3_CBC, env.getEncryptionAlgOID());
-    }
-    
-    public static Test suite()
-        throws Exception
-    {
-        return new CMSTestSetup(new TestSuite(EnvelopedDataStreamTest.class));
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/EnvelopedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/EnvelopedDataTest.java
deleted file mode 100644
index dea5d92..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/EnvelopedDataTest.java
+++ /dev/null
@@ -1,1002 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.io.IOException;
-import java.security.GeneralSecurityException;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-import java.security.Security;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-
-import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
-import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
-import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.cms.CMSEnvelopedData;
-import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
-import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.cms.CMSPBEKey;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.KeyTransRecipientInformation;
-import org.bouncycastle.cms.PKCS5Scheme2PBEKey;
-import org.bouncycastle.cms.PKCS5Scheme2UTF8PBEKey;
-import org.bouncycastle.cms.PasswordRecipientInformation;
-import org.bouncycastle.cms.RecipientId;
-import org.bouncycastle.cms.RecipientInformation;
-import org.bouncycastle.cms.RecipientInformationStore;
-import org.bouncycastle.cms.jcajce.JceKeyAgreeRecipientId;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.encoders.Hex;
-
-public class EnvelopedDataTest
-    extends TestCase 
-{
-    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
-
-    private static String          _signDN;
-    private static KeyPair         _signKP;  
-    private static X509Certificate _signCert;
-
-    private static String          _origDN;
-    private static KeyPair         _origKP;
-    private static X509Certificate _origCert;
-
-    private static String          _reciDN;
-    private static String          _reciDN2;
-    private static KeyPair         _reciKP;
-    private static X509Certificate _reciCert;
-
-    private static KeyPair         _origEcKP;
-    private static KeyPair         _reciEcKP;
-    private static X509Certificate _reciEcCert;
-    private static KeyPair         _reciEcKP2;
-    private static X509Certificate _reciEcCert2;
-
-    private static boolean         _initialised = false;
-
-    private byte[] oldKEK = Base64.decode(
-                          "MIAGCSqGSIb3DQEHA6CAMIACAQIxQaI/MD0CAQQwBwQFAQIDBAUwDQYJYIZIAWUDBAEFBQAEI"
-                        + "Fi2eHTPM4bQSjP4DUeDzJZLpfemW2gF1SPq7ZPHJi1mMIAGCSqGSIb3DQEHATAUBggqhkiG9w"
-                        + "0DBwQImtdGyUdGGt6ggAQYk9X9z01YFBkU7IlS3wmsKpm/zpZClTceAAAAAAAAAAAAAA==");
-
-    private byte[] ecKeyAgreeMsgAES256 = Base64.decode(
-           "MIAGCSqGSIb3DQEHA6CAMIACAQIxgcShgcECAQOgQ6FBMAsGByqGSM49AgEF"
-         + "AAMyAAPdXlSTpub+qqno9hUGkUDl+S3/ABhPziIB5yGU4678tgOgU5CiKG9Z"
-         + "kfnabIJ3nZYwGgYJK4EFEIZIPwACMA0GCWCGSAFlAwQBLQUAMFswWTAtMCgx"
-         + "EzARBgNVBAMTCkFkbWluLU1EU0UxETAPBgNVBAoTCDRCQ1QtMklEAgEBBCi/"
-         + "rJRLbFwEVW6PcLLmojjW9lI/xGD7CfZzXrqXFw8iHaf3hTRau1gYMIAGCSqG"
-         + "SIb3DQEHATAdBglghkgBZQMEASoEEMtCnKKPwccmyrbgeSIlA3qggAQQDLw8"
-         + "pNJR97bPpj6baG99bQQQwhEDsoj5Xg1oOxojHVcYzAAAAAAAAAAAAAA=");
-
-    private byte[] ecKeyAgreeMsgAES128 = Base64.decode(
-           "MIAGCSqGSIb3DQEHA6CAMIACAQIxgbShgbECAQOgQ6FBMAsGByqGSM49AgEF"
-         + "AAMyAAL01JLEgKvKh5rbxI/hOxs/9WEezMIsAbUaZM4l5tn3CzXAN505nr5d"
-         + "LhrcurMK+tAwGgYJK4EFEIZIPwACMA0GCWCGSAFlAwQBBQUAMEswSTAtMCgx"
-         + "EzARBgNVBAMTCkFkbWluLU1EU0UxETAPBgNVBAoTCDRCQ1QtMklEAgEBBBhi"
-         + "FLjc5g6aqDT3f8LomljOwl1WTrplUT8wgAYJKoZIhvcNAQcBMB0GCWCGSAFl"
-         + "AwQBAgQQzXjms16Y69S/rB0EbHqRMaCABBAFmc/QdVW6LTKdEy97kaZzBBBa"
-         + "fQuviUS03NycpojELx0bAAAAAAAAAAAAAA==");
-
-    private byte[] ecKeyAgreeMsgDESEDE = Base64.decode(
-           "MIAGCSqGSIb3DQEHA6CAMIACAQIxgcahgcMCAQOgQ6FBMAsGByqGSM49AgEF"
-         + "AAMyAALIici6Nx1WN5f0ThH2A8ht9ovm0thpC5JK54t73E1RDzCifePaoQo0"
-         + "xd6sUqoyGaYwHAYJK4EFEIZIPwACMA8GCyqGSIb3DQEJEAMGBQAwWzBZMC0w"
-         + "KDETMBEGA1UEAxMKQWRtaW4tTURTRTERMA8GA1UEChMINEJDVC0ySUQCAQEE"
-         + "KJuqZQ1NB1vXrKPOnb4TCpYOsdm6GscWdwAAZlm2EHMp444j0s55J9wwgAYJ"
-         + "KoZIhvcNAQcBMBQGCCqGSIb3DQMHBAjwnsDMsafCrKCABBjyPvqFOVMKxxut"
-         + "VfTx4fQlNGJN8S2ATRgECMcTQ/dsmeViAAAAAAAAAAAAAA==");
-
-   private byte[] ecMQVKeyAgreeMsgAES128 = Base64.decode(
-          "MIAGCSqGSIb3DQEHA6CAMIACAQIxgf2hgfoCAQOgQ6FBMAsGByqGSM49AgEF"
-        + "AAMyAAPDKU+0H58tsjpoYmYCInMr/FayvCCkupebgsnpaGEB7qS9vzcNVUj6"
-        + "mrnmiC2grpmhRwRFMEMwQTALBgcqhkjOPQIBBQADMgACZpD13z9c7DzRWx6S"
-        + "0xdbq3S+EJ7vWO+YcHVjTD8NcQDcZcWASW899l1PkL936zsuMBoGCSuBBRCG"
-        + "SD8AEDANBglghkgBZQMEAQUFADBLMEkwLTAoMRMwEQYDVQQDEwpBZG1pbi1N"
-        + "RFNFMREwDwYDVQQKEwg0QkNULTJJRAIBAQQYFq58L71nyMK/70w3nc6zkkRy"
-        + "RL7DHmpZMIAGCSqGSIb3DQEHATAdBglghkgBZQMEAQIEEDzRUpreBsZXWHBe"
-        + "onxOtSmggAQQ7csAZXwT1lHUqoazoy8bhAQQq+9Zjj8iGdOWgyebbfj67QAA"
-        + "AAAAAAAAAAA=");
-
-
-    private byte[] ecKeyAgreeKey = Base64.decode(
-        "MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDC8vp7xVTbKSgYVU5Wc"
-      + "hGkWbzaj+yUFETIWP1Dt7+WSpq3ikSPdl7PpHPqnPVZfoIWhZANiAgSYHTgxf+Dd"
-      + "Tt84dUvuSKkFy3RhjxJmjwIscK6zbEUzKhcPQG2GHzXhWK5x1kov0I74XpGhVkya"
-      + "ElH5K6SaOXiXAzcyNGggTOk4+ZFnz5Xl0pBje3zKxPhYu0SnCw7Pcqw=");
-
-    private byte[] bobPrivRsaEncrypt = Base64.decode(
-       "MIIChQIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAKnhZ5g/OdVf"
-     + "8qCTQV6meYmFyDVdmpFb+x0B2hlwJhcPvaUi0DWFbXqYZhRBXM+3twg7CcmR"
-     + "uBlpN235ZR572akzJKN/O7uvRgGGNjQyywcDWVL8hYsxBLjMGAgUSOZPHPtd"
-     + "YMTgXB9T039T2GkB8QX4enDRvoPGXzjPHCyqaqfrAgMBAAECgYBnzUhMmg2P"
-     + "mMIbZf8ig5xt8KYGHbztpwOIlPIcaw+LNd4Ogngwy+e6alatd8brUXlweQqg"
-     + "9P5F4Kmy9Bnah5jWMIR05PxZbMHGd9ypkdB8MKCixQheIXFD/A0HPfD6bRSe"
-     + "TmPwF1h5HEuYHD09sBvf+iU7o8AsmAX2EAnYh9sDGQJBANDDIsbeopkYdo+N"
-     + "vKZ11mY/1I1FUox29XLE6/BGmvE+XKpVC5va3Wtt+Pw7PAhDk7Vb/s7q/WiE"
-     + "I2Kv8zHCueUCQQDQUfweIrdb7bWOAcjXq/JY1PeClPNTqBlFy2bKKBlf4hAr"
-     + "84/sajB0+E0R9KfEILVHIdxJAfkKICnwJAiEYH2PAkA0umTJSChXdNdVUN5q"
-     + "SO8bKlocSHseIVnDYDubl6nA7xhmqU5iUjiEzuUJiEiUacUgFJlaV/4jbOSn"
-     + "I3vQgLeFAkEAni+zN5r7CwZdV+EJBqRd2ZCWBgVfJAZAcpw6iIWchw+dYhKI"
-     + "FmioNRobQ+g4wJhprwMKSDIETukPj3d9NDAlBwJAVxhn1grStavCunrnVNqc"
-     + "BU+B1O8BiR4yPWnLMcRSyFRVJQA7HCp8JlDV6abXd8vPFfXuC9WN7rOvTKF8"
-     + "Y0ZB9qANMAsGA1UdDzEEAwIAEA==");
-
-    private byte[] rfc4134ex5_1 = Base64.decode(
-          "MIIBHgYJKoZIhvcNAQcDoIIBDzCCAQsCAQAxgcAwgb0CAQAwJjASMRAwDgYD"
-        + "VQQDEwdDYXJsUlNBAhBGNGvHgABWvBHTbi7NXXHQMA0GCSqGSIb3DQEBAQUA"
-        + "BIGAC3EN5nGIiJi2lsGPcP2iJ97a4e8kbKQz36zg6Z2i0yx6zYC4mZ7mX7FB"
-        + "s3IWg+f6KgCLx3M1eCbWx8+MDFbbpXadCDgO8/nUkUNYeNxJtuzubGgzoyEd"
-        + "8Ch4H/dd9gdzTd+taTEgS0ipdSJuNnkVY4/M652jKKHRLFf02hosdR8wQwYJ"
-        + "KoZIhvcNAQcBMBQGCCqGSIb3DQMHBAgtaMXpRwZRNYAgDsiSf8Z9P43LrY4O"
-        + "xUk660cu1lXeCSFOSOpOJ7FuVyU=");
-
-    private byte[] rfc4134ex5_2 = Base64.decode(
-            "MIIBZQYJKoZIhvcNAQcDoIIBVjCCAVICAQIxggEAMIG9AgEAMCYwEjEQMA4G"
-         + "A1UEAxMHQ2FybFJTQQIQRjRrx4AAVrwR024uzV1x0DANBgkqhkiG9w0BAQEF"
-         + "AASBgJQmQojGi7Z4IP+CVypBmNFoCDoEp87khtgyff2N4SmqD3RxPx+8hbLQ"
-         + "t9i3YcMwcap+aiOkyqjMalT03VUC0XBOGv+HYI3HBZm/aFzxoq+YOXAWs5xl"
-         + "GerZwTOc9j6AYlK4qXvnztR5SQ8TBjlzytm4V7zg+TGrnGVNQBNw47Ewoj4C"
-         + "AQQwDQQLTWFpbExpc3RSQzIwEAYLKoZIhvcNAQkQAwcCAToEGHcUr5MSJ/g9"
-         + "HnJVHsQ6X56VcwYb+OfojTBJBgkqhkiG9w0BBwEwGgYIKoZIhvcNAwIwDgIC"
-         + "AKAECJwE0hkuKlWhgCBeKNXhojuej3org9Lt7n+wWxOhnky5V50vSpoYRfRR"
-         + "yw==");
-    
-    public EnvelopedDataTest()
-    {
-    }
-
-    private static void init()
-        throws Exception
-    {
-        if (!_initialised)
-        {
-            _initialised = true;
-            
-            _signDN   = "O=Bouncy Castle, C=AU";
-            _signKP   = CMSTestUtil.makeKeyPair();  
-            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
-
-            _origDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
-            _origKP   = CMSTestUtil.makeKeyPair();
-            _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _signKP, _signDN);
-
-            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciDN2  = "CN=Fred, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciKP   = CMSTestUtil.makeKeyPair();
-            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
-
-            _origEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcKP = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcCert = CMSTestUtil.makeCertificate(_reciEcKP, _reciDN, _signKP, _signDN);
-            _reciEcKP2 = CMSTestUtil.makeEcDsaKeyPair();
-            _reciEcCert2 = CMSTestUtil.makeCertificate(_reciEcKP2, _reciDN2, _signKP, _signDN);
-        }
-    }
-    
-    public static void main(
-        String args[])
-        throws Exception
-    {
-        junit.textui.TestRunner.run(EnvelopedDataTest.suite());
-    }
-
-    public static Test suite() 
-        throws Exception
-    {
-        init();
-        
-        return new CMSTestSetup(new TestSuite(EnvelopedDataTest.class));
-    }
-
-    public void testKeyTrans()
-        throws Exception
-    {
-        byte[]          data     = "WallaWallaWashington".getBytes();
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                CMSEnvelopedDataGenerator.DES_EDE3_CBC, BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-        
-        Collection  c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator    it = c.iterator();
-
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-    }
-
-    public void testKeyTransCAST5SunJCE()
-        throws Exception
-    {
-        if (Security.getProvider("SunJCE") == null)
-        {
-            return;
-        }
-        
-        String version = System.getProperty("java.version");
-        if (version.startsWith("1.4") || version.startsWith("1.3"))
-        {
-            return;
-        }
-        
-        byte[]          data     = "WallaWallaWashington".getBytes();
-    
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-    
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                CMSEnvelopedDataGenerator.CAST5_CBC, "SunJCE");
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-        
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.CAST5_CBC);
-
-        Collection  c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator    it = c.iterator();
-        
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-    
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-            
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), "SunJCE");
-    
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-    }
-
-    public void testKeyTransRC4()
-        throws Exception
-    {
-        byte[]          data     = "WallaWallaBouncyCastle".getBytes();
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                "1.2.840.113549.3.4", BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(), "1.2.840.113549.3.4");
-        
-        Collection  c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator    it = c.iterator();
-
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-    }
-    
-    public void testKeyTrans128RC4()
-        throws Exception
-    {
-        byte[]          data     = "WallaWallaBouncyCastle".getBytes();
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                "1.2.840.113549.3.4", 128, BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(), "1.2.840.113549.3.4");
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-    
-    public void testKeyTransODES()
-        throws Exception
-    {
-        byte[]          data     = "WallaWallaBouncyCastle".getBytes();
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                "1.3.14.3.2.7", BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(), "1.3.14.3.2.7");
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testKeyTransSmallAES()
-        throws Exception
-    {
-        byte[]          data     = new byte[] { 0, 1, 2, 3 };
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                              new CMSProcessableByteArray(data),
-                              CMSEnvelopedDataGenerator.AES128_CBC, BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(),
-                                   CMSEnvelopedDataGenerator.AES128_CBC);
-        
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testKeyTransCAST5()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.CAST5_CBC, new DERObjectIdentifier(CMSEnvelopedDataGenerator.CAST5_CBC), ASN1Sequence.class);
-    }
-
-    public void testKeyTransAES128()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.AES128_CBC, NISTObjectIdentifiers.id_aes128_CBC, DEROctetString.class);
-    }
-
-    public void testKeyTransAES192()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.AES192_CBC, NISTObjectIdentifiers.id_aes192_CBC, DEROctetString.class);
-    }
-
-    public void testKeyTransAES256()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.AES256_CBC, NISTObjectIdentifiers.id_aes256_CBC, DEROctetString.class);
-    }
-
-    public void testKeyTransSEED()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.SEED_CBC, KISAObjectIdentifiers.id_seedCBC, DEROctetString.class);
-    }
-
-    public void testKeyTransCamellia128()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.CAMELLIA128_CBC, NTTObjectIdentifiers.id_camellia128_cbc, DEROctetString.class);
-    }
-
-    public void testKeyTransCamellia192()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.CAMELLIA192_CBC, NTTObjectIdentifiers.id_camellia192_cbc, DEROctetString.class);
-    }
-
-    public void testKeyTransCamellia256()
-        throws Exception
-    {
-        tryKeyTrans(CMSEnvelopedDataGenerator.CAMELLIA256_CBC, NTTObjectIdentifiers.id_camellia256_cbc, DEROctetString.class);
-    }
-
-    private void tryKeyTrans(String generatorOID, DERObjectIdentifier checkOID, Class asn1Params)
-        throws Exception
-    {
-        byte[]          data     = "WallaWallaWashington".getBytes();
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyTransRecipient(_reciCert);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                generatorOID, BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(checkOID.getId(), ed.getEncryptionAlgOID());
-
-        if (asn1Params != null)
-        {
-            ASN1InputStream aIn = new ASN1InputStream(ed.getEncryptionAlgParams());
-
-            assertTrue(asn1Params.isAssignableFrom(aIn.readObject().getClass()));
-        }
-
-        Collection  c = recipients.getRecipients();
-
-        assertEquals(1, c.size());
-
-        Iterator    it = c.iterator();
-
-        if (!it.hasNext())
-        {
-            fail("no recipients found");
-        }
-
-        while (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
-
-            byte[] recData = recipient.getContent(_reciKP.getPrivate(), BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-    }
-
-    public void testErrorneousKEK()
-        throws Exception
-    {
-        byte[]    data = "WallaWallaWashington".getBytes();
-        SecretKey kek  = new SecretKeySpec(new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 }, "AES");
-
-        CMSEnvelopedData ed = new CMSEnvelopedData(oldKEK);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals(recipient.getKeyEncryptionAlgOID(), NISTObjectIdentifiers.id_aes128_wrap.getId());
-
-            byte[] recData = recipient.getContent(kek, BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testDESKEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeDesede192Key(), new DERObjectIdentifier("1.2.840.113549.1.9.16.3.6"));
-    }
-    public void testRC2128KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeRC2128Key(), new DERObjectIdentifier("1.2.840.113549.1.9.16.3.7"));
-    }
-
-    public void testAES128KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeAESKey(128), NISTObjectIdentifiers.id_aes128_wrap);
-    }
-
-    public void testAES192KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeAESKey(192), NISTObjectIdentifiers.id_aes192_wrap);
-    }
-
-    public void testAES256KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeAESKey(256), NISTObjectIdentifiers.id_aes256_wrap);
-    }
-
-    public void testSEED128KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeSEEDKey(), KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap);
-    }
-
-    public void testCamellia128KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeCamelliaKey(128), NTTObjectIdentifiers.id_camellia128_wrap);
-    }
-
-    public void testCamellia192KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeCamelliaKey(192), NTTObjectIdentifiers.id_camellia192_wrap);
-    }
-
-    public void testCamellia256KEK()
-        throws Exception
-    {
-        tryKekAlgorithm(CMSTestUtil.makeCamelliaKey(256), NTTObjectIdentifiers.id_camellia256_wrap);
-    }
-
-    private void tryKekAlgorithm(SecretKey kek, DERObjectIdentifier algOid)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        byte[]    data = "WallaWallaWashington".getBytes();
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        byte[]  kekId = new byte[] { 1, 2, 3, 4, 5 };
-
-        edGen.addKEKRecipient(kek, kekId);
-
-        CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                CMSEnvelopedDataGenerator.DES_EDE3_CBC, BC);
-
-        RecipientInformationStore recipients = ed.getRecipientInfos();
-
-        Collection c = recipients.getRecipients();
-        Iterator it = c.iterator();
-
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-
-        if (it.hasNext())
-        {
-            RecipientInformation recipient = (RecipientInformation)it.next();
-
-            assertEquals(algOid.getId(), recipient.getKeyEncryptionAlgOID());
-
-            byte[] recData = recipient.getContent(kek, BC);
-
-            assertTrue(Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testECKeyAgree()
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyAgreementRecipient(CMSEnvelopedDataGenerator.ECDH_SHA1KDF,
-            _origEcKP.getPrivate(), _origEcKP.getPublic(),
-            _reciEcCert, CMSEnvelopedDataGenerator.AES128_WRAP, BC);
-
-        CMSEnvelopedData ed = edGen.generate(
-            new CMSProcessableByteArray(data),
-            CMSEnvelopedDataGenerator.AES128_CBC, BC);
-
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
-
-        RecipientInformationStore recipients = ed.getRecipientInfos();
-
-        confirmDataReceived(recipients, data, _reciEcCert, _reciEcKP.getPrivate(), BC);
-        confirmNumberRecipients(recipients, 1);
-    }
-
-    public void testECMQVKeyAgree()
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addKeyAgreementRecipient(CMSEnvelopedDataGenerator.ECMQV_SHA1KDF,
-            _origEcKP.getPrivate(), _origEcKP.getPublic(),
-            _reciEcCert, CMSEnvelopedDataGenerator.AES128_WRAP, BC);
-
-        CMSEnvelopedData ed = edGen.generate(
-            new CMSProcessableByteArray(data),
-            CMSEnvelopedDataGenerator.AES128_CBC, BC);
-
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
-
-        RecipientInformationStore recipients = ed.getRecipientInfos();
-
-        confirmDataReceived(recipients, data, _reciEcCert, _reciEcKP.getPrivate(), BC);
-        confirmNumberRecipients(recipients, 1);
-    }
-
-    public void testECMQVKeyAgreeMultiple()
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        ArrayList recipientCerts = new ArrayList();
-        recipientCerts.add(_reciEcCert);
-        recipientCerts.add(_reciEcCert2);
-
-        edGen.addKeyAgreementRecipients(CMSEnvelopedDataGenerator.ECMQV_SHA1KDF,
-            _origEcKP.getPrivate(), _origEcKP.getPublic(),
-            recipientCerts, CMSEnvelopedDataGenerator.AES128_WRAP, BC);
-
-        CMSEnvelopedData ed = edGen.generate(
-            new CMSProcessableByteArray(data),
-            CMSEnvelopedDataGenerator.AES128_CBC, BC);
-
-        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.AES128_CBC);
-
-        RecipientInformationStore recipients = ed.getRecipientInfos();
-
-        confirmDataReceived(recipients, data, _reciEcCert, _reciEcKP.getPrivate(), BC);
-        confirmDataReceived(recipients, data, _reciEcCert2, _reciEcKP2.getPrivate(), BC);
-        confirmNumberRecipients(recipients, 2);
-    }
-
-    private static void confirmDataReceived(RecipientInformationStore recipients,
-        byte[] expectedData, X509Certificate reciCert, PrivateKey reciPrivKey, String provider)
-        throws CMSException, NoSuchProviderException, CertificateEncodingException, IOException
-    {
-        RecipientId rid = new JceKeyAgreeRecipientId(reciCert);
-
-        RecipientInformation recipient = recipients.get(rid);
-        assertNotNull(recipient);
-
-        byte[] actualData = recipient.getContent(reciPrivKey, provider);
-        assertEquals(true, Arrays.equals(expectedData, actualData));
-    }
-
-    private static void confirmNumberRecipients(RecipientInformationStore recipients, int count)
-    {
-        assertEquals(count, recipients.getRecipients().size());
-    }
-
-    public void testECKeyAgreeVectors()
-        throws Exception
-    {
-        PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(ecKeyAgreeKey);
-        KeyFactory          fact = KeyFactory.getInstance("ECDH", BC);
-        PrivateKey          privKey = fact.generatePrivate(privSpec);
-
-        verifyECKeyAgreeVectors(privKey, "2.16.840.1.101.3.4.1.42", ecKeyAgreeMsgAES256);
-        verifyECKeyAgreeVectors(privKey, "2.16.840.1.101.3.4.1.2", ecKeyAgreeMsgAES128);
-        verifyECKeyAgreeVectors(privKey, "1.2.840.113549.3.7", ecKeyAgreeMsgDESEDE);
-    }
-
-    public void testECMQVKeyAgreeVectors()
-        throws Exception
-    {
-        PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(ecKeyAgreeKey);
-        KeyFactory          fact = KeyFactory.getInstance("ECDH", BC);
-        PrivateKey          privKey = fact.generatePrivate(privSpec);
-
-        verifyECMQVKeyAgreeVectors(privKey, "2.16.840.1.101.3.4.1.2", ecMQVKeyAgreeMsgAES128);
-    }
-
-    public void testPasswordAES256()
-        throws Exception
-    {
-        passwordTest(CMSEnvelopedDataGenerator.AES256_CBC);
-        passwordUTF8Test(CMSEnvelopedDataGenerator.AES256_CBC);
-    }
-
-    public void testPasswordDESEDE()
-        throws Exception
-    {
-        passwordTest(CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-        passwordUTF8Test(CMSEnvelopedDataGenerator.DES_EDE3_CBC);
-    }
-
-    public void testRFC4134ex5_1()
-        throws Exception
-    {
-        byte[] data = Hex.decode("5468697320697320736f6d652073616d706c6520636f6e74656e742e");
-
-        KeyFactory kFact = KeyFactory.getInstance("RSA", BC);
-        Key key = kFact.generatePrivate(new PKCS8EncodedKeySpec(bobPrivRsaEncrypt));
-
-        CMSEnvelopedData ed = new CMSEnvelopedData(rfc4134ex5_1);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals("1.2.840.113549.3.7", ed.getEncryptionAlgOID());
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(key, BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testRFC4134ex5_2()
-        throws Exception
-    {
-        byte[] data = Hex.decode("5468697320697320736f6d652073616d706c6520636f6e74656e742e");
-
-        KeyFactory kFact = KeyFactory.getInstance("RSA", BC);
-        Key key = kFact.generatePrivate(new PKCS8EncodedKeySpec(bobPrivRsaEncrypt));
-
-        CMSEnvelopedData ed = new CMSEnvelopedData(rfc4134ex5_2);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals("1.2.840.113549.3.2", ed.getEncryptionAlgOID());
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            while (it.hasNext())
-            {
-                RecipientInformation   recipient = (RecipientInformation)it.next();
-                byte[] recData;
-
-                if (recipient instanceof KeyTransRecipientInformation)
-                {
-                    recData = recipient.getContent(key, BC);
-
-                    assertEquals(true, Arrays.equals(data, recData));
-                }
-            }
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    public void testOriginatorInfo()
-        throws Exception
-    {
-        CMSEnvelopedData env = new CMSEnvelopedData(CMSSampleMessages.originatorMessage);
-
-        RecipientInformationStore  recipients = env.getRecipientInfos();
-
-        assertEquals(CMSEnvelopedDataGenerator.DES_EDE3_CBC, env.getEncryptionAlgOID());
-
-    }
-
-    private void passwordTest(String algorithm)
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addPasswordRecipient(new PKCS5Scheme2PBEKey("password".toCharArray(), new byte[20], 5), algorithm);
-
-        CMSEnvelopedData ed = edGen.generate(
-                              new CMSProcessableByteArray(data),
-                              CMSEnvelopedDataGenerator.AES128_CBC, BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(),
-                                   CMSEnvelopedDataGenerator.AES128_CBC);
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            PasswordRecipientInformation recipient = (PasswordRecipientInformation)it.next();
-
-            CMSPBEKey key = new PKCS5Scheme2PBEKey("password".toCharArray(),
-                recipient.getKeyDerivationAlgParameters(BC));
-
-            byte[] recData = recipient.getContent(key, BC);
-
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-
-        //
-        // try algorithm parameters constructor
-        //
-        it = c.iterator();
-
-        RecipientInformation   recipient = (RecipientInformation)it.next();
-
-        byte[] recData = recipient.getContent(new PKCS5Scheme2PBEKey("password".toCharArray(), ((PasswordRecipientInformation)recipient).getKeyDerivationAlgParameters(BC)), BC);
-        assertEquals(true, Arrays.equals(data, recData));
-    }
-
-    private void passwordUTF8Test(String algorithm)
-        throws Exception
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
-
-        edGen.addPasswordRecipient(new PKCS5Scheme2UTF8PBEKey("abc\u5639\u563b".toCharArray(), new byte[20], 5), algorithm);
-
-        CMSEnvelopedData ed = edGen.generate(
-                              new CMSProcessableByteArray(data),
-                              CMSEnvelopedDataGenerator.AES128_CBC, BC);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        assertEquals(ed.getEncryptionAlgOID(),
-                                   CMSEnvelopedDataGenerator.AES128_CBC);
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            byte[] recData = recipient.getContent(new PKCS5Scheme2UTF8PBEKey("abc\u5639\u563b".toCharArray(), new byte[20], 5), BC);
-            assertEquals(true, Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-
-        //
-        // try algorithm parameters constructor
-        //
-        it = c.iterator();
-
-        RecipientInformation   recipient = (RecipientInformation)it.next();
-
-        byte[] recData = recipient.getContent(new PKCS5Scheme2UTF8PBEKey("abc\u5639\u563b".toCharArray(), ((PasswordRecipientInformation)recipient).getKeyDerivationAlgParameters(BC)), BC);
-        assertEquals(true, Arrays.equals(data, recData));
-    }
-
-    private void verifyECKeyAgreeVectors(PrivateKey privKey, String wrapAlg, byte[] message)
-        throws CMSException, GeneralSecurityException
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedData ed = new CMSEnvelopedData(message);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        assertEquals(wrapAlg, ed.getEncryptionAlgOID());
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals("1.3.133.16.840.63.0.2", recipient.getKeyEncryptionAlgOID());
-
-            byte[] recData = recipient.getContent(privKey, BC);
-
-            assertTrue(Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-
-    private void verifyECMQVKeyAgreeVectors(PrivateKey privKey, String wrapAlg, byte[] message)
-        throws CMSException, GeneralSecurityException
-    {
-        byte[] data = Hex.decode("504b492d4320434d5320456e76656c6f706564446174612053616d706c65");
-
-        CMSEnvelopedData ed = new CMSEnvelopedData(message);
-
-        RecipientInformationStore  recipients = ed.getRecipientInfos();
-
-        Collection  c = recipients.getRecipients();
-        Iterator    it = c.iterator();
-
-        assertEquals(wrapAlg, ed.getEncryptionAlgOID());
-
-        if (it.hasNext())
-        {
-            RecipientInformation   recipient = (RecipientInformation)it.next();
-
-            assertEquals("1.3.133.16.840.63.0.16", recipient.getKeyEncryptionAlgOID());
-
-            byte[] recData = recipient.getContent(privKey, BC);
-
-            assertTrue(Arrays.equals(data, recData));
-        }
-        else
-        {
-            fail("no recipient found");
-        }
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/MiscDataStreamTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/MiscDataStreamTest.java
index 7efaec7..4a86cac 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/MiscDataStreamTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/MiscDataStreamTest.java
@@ -5,8 +5,6 @@
 import java.io.OutputStream;
 import java.security.KeyPair;
 import java.security.MessageDigest;
-import java.security.cert.CertStore;
-import java.security.cert.CollectionCertStoreParameters;
 import java.security.cert.X509CRL;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
@@ -17,6 +15,9 @@
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaCRLStore;
+import org.bouncycastle.cert.jcajce.JcaCertStore;
 import org.bouncycastle.cms.CMSCompressedDataStreamGenerator;
 import org.bouncycastle.cms.CMSDigestedData;
 import org.bouncycastle.cms.CMSSignedDataParser;
@@ -24,10 +25,16 @@
 import org.bouncycastle.cms.CMSTypedStream;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
 import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
+import org.bouncycastle.cms.jcajce.ZlibCompressor;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Store;
 import org.bouncycastle.util.encoders.Base64;
 
 public class MiscDataStreamTest
@@ -74,6 +81,20 @@
 
     private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
 
+    private static final DigestCalculatorProvider digCalcProv;
+
+    static
+    {
+        try
+        {
+            digCalcProv =  new JcaDigestCalculatorProviderBuilder().build();
+        }
+        catch (OperatorCreationException e)
+        {
+            throw new IllegalStateException("can't create default provider!!!");
+        }
+    }
+
     public MiscDataStreamTest(String name)
     {
         super(name);
@@ -109,7 +130,7 @@
     private void verifySignatures(CMSSignedDataParser sp, byte[] contentDigest)
         throws Exception
     {
-        CertStore               certStore = sp.getCertificatesAndCRLs("Collection", BC);
+        Store                   certStore = sp.getCertificates();
         SignerInformationStore  signers = sp.getSignerInfos();
 
         Collection              c = signers.getSigners();
@@ -118,24 +139,18 @@
         while (it.hasNext())
         {
             SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certStore.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            Collection          certCollection = certStore.getMatches(signer.getSID());
 
             Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
+            X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
 
-            assertEquals(true, signer.verify(cert, BC));
+            assertEquals(true, signer.verify(new JcaSignerInfoVerifierBuilder(digCalcProv).setProvider(BC).build(cert)));
 
             if (contentDigest != null)
             {
                 assertTrue(MessageDigest.isEqual(contentDigest, signer.getContentDigest()));
             }
         }
-
-        Collection certColl = certStore.getCertificates(null);
-        Collection crlColl = certStore.getCRLs(null);
-
-        assertEquals(certColl.size(), sp.getCertificates("Collection", BC).getMatches(null).size());
-        assertEquals(crlColl.size(), sp.getCRLs("Collection", BC).getMatches(null).size());
     }
 
     private void verifySignatures(CMSSignedDataParser sp)
@@ -148,7 +163,7 @@
         throws Exception
     {
         CMSSignedDataParser sp;
-        sp = new CMSSignedDataParser(bOut.toByteArray());
+        sp = new CMSSignedDataParser(digCalcProv, bOut.toByteArray());
 
         sp.getSignedContent().drain();
 
@@ -160,14 +175,14 @@
     private void checkSigParseable(byte[] sig)
         throws Exception
     {
-        CMSSignedDataParser sp = new CMSSignedDataParser(sig);
+        CMSSignedDataParser sp = new CMSSignedDataParser(digCalcProv, sig);
         sp.getVersion();
         CMSTypedStream sc = sp.getSignedContent();
         if (sc != null)
         {
             sc.drain();
         }
-        sp.getCertificatesAndCRLs("Collection", BC);
+        sp.getCertificates();
         sp.getSignerInfos();
         sp.close();
     }
@@ -176,28 +191,27 @@
         throws Exception
     {
         List                  certList = new ArrayList();
+        List                  crlList = new ArrayList();
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
         certList.add(_origCert);
         certList.add(_signCert);
 
-        certList.add(_signCrl);
-        certList.add(_origCrl);
-
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
+        crlList.add(_signCrl);
+        crlList.add(_origCrl);
 
         CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
 
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
+        gen.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider(BC).build("SHA1withRSA", _origKP.getPrivate(), _origCert));
 
-        gen.addCertificatesAndCRLs(certsAndCrls);
+        gen.addCertificates(new JcaCertStore(certList));
+        gen.addCRLs(new JcaCRLStore(crlList));
 
         OutputStream sigOut = gen.open(bOut);
 
         CMSCompressedDataStreamGenerator cGen = new CMSCompressedDataStreamGenerator();
 
-        OutputStream cOut = cGen.open(sigOut, CMSCompressedDataStreamGenerator.ZLIB);
+        OutputStream cOut = cGen.open(sigOut, new ZlibCompressor());
 
         cOut.write(TEST_MESSAGE.getBytes());
 
@@ -210,13 +224,13 @@
         // generate compressed stream
         ByteArrayOutputStream cDataOut = new ByteArrayOutputStream();
         
-        cOut = cGen.open(cDataOut, CMSCompressedDataStreamGenerator.ZLIB);
+        cOut = cGen.open(cDataOut, new ZlibCompressor());
 
         cOut.write(TEST_MESSAGE.getBytes());
 
         cOut.close();
 
-        CMSSignedDataParser     sp = new CMSSignedDataParser(
+        CMSSignedDataParser     sp = new CMSSignedDataParser(digCalcProv,
                 new CMSTypedStream(new ByteArrayInputStream(cDataOut.toByteArray())), bOut.toByteArray());
 
         sp.getSignedContent().drain();
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
index c29293a..d95499d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
@@ -10,6 +10,7 @@
 import java.security.PrivateKey;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
+import java.security.spec.MGF1ParameterSpec;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.util.Arrays;
 import java.util.Collection;
@@ -17,6 +18,8 @@
 import java.util.Iterator;
 
 import javax.crypto.SecretKey;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
 import javax.crypto.spec.SecretKeySpec;
 
 import junit.framework.Test;
@@ -26,7 +29,6 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.DERUTF8String;
@@ -36,8 +38,10 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
 import org.bouncycastle.asn1.x500.X500Name;
-import org.bouncycastle.asn1.x509.X509Extension;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cms.CMSAlgorithm;
@@ -69,6 +73,7 @@
 import org.bouncycastle.cms.jcajce.JcePasswordRecipientInfoGenerator;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.operator.jcajce.JcaAlgorithmParametersConverter;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -88,7 +93,9 @@
     private static String          _reciDN;
     private static String          _reciDN2;
     private static KeyPair         _reciKP;
+    private static KeyPair         _reciOaepKP;
     private static X509Certificate _reciCert;
+    private static X509Certificate _reciCertOaep;
 
     private static KeyPair         _origEcKP;
     private static KeyPair         _reciEcKP;
@@ -185,6 +192,32 @@
          + "AKAECJwE0hkuKlWhgCBeKNXhojuej3org9Lt7n+wWxOhnky5V50vSpoYRfRR"
          + "yw==");
 
+    private byte[] tooShort3DES = Base64.decode(
+            "MIAGCSqGSIb3DQEHA6CAMIACAQAxgcQwgcECAQAwKjAlMRYwFAYDVQQKDA1C" +
+            "b3VuY3kgQ2FzdGxlMQswCQYDVQQGEwJBVQIBCjANBgkqhkiG9w0BAQEFAASB" +
+            "gJIM2QN0o6iv8Ux018pVCJ8js+ROV4t6+KoMwLJ4DzRKLU8XCAb9BS+crP+F" +
+            "ghNTxTpTX8TaxPrO4wV0USgVHu2SvFnxNaWZjBDVIyZI2HR4QkSTqFMhsUB2" +
+            "6CuZIWBZkhqQ6ruDfvn9UuBWVnfsBD4iryZ1idr713sDeVo5TyvTMIAGCSqG" +
+            "SIb3DQEHATAUBggqhkiG9w0DBwQIQq9e4+WB3CqggAQIwU4cOlmkWUcAAAAA" +
+            "AAAAAAAA");
+
+    private byte[] tooShort3DESKey = Base64.decode(
+            "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAODZDCj0nQdV" +
+            "f0GGeFsPjjvPx1Vem0V6IkJ4SzazGKfddk0pX58ZDCnG+S+OPiXmPDqValiu" +
+            "9FtNy2/r9rrf/6qtcVQJkfSJv9E5Y7HgI98L/Y9lKxZWsfRqu/SlYO5zx0Dc" +
+            "2rzDvvZRtrtaq0uuHXWJlbWda2L9S65sv/Le/zvjAgMBAAECgYEAnn+iGMTG" +
+            "ZMMaH6Cg+t/uTa9cPougPMuplt2hd3+sY7izihUeONK5RkHiqmlE2gaAcnOd" +
+            "McKysiIWxGC73mPEnsOObPkaFlneVb5CtjTaTMdptuLNEQkwvtKhuW2HnMra" +
+            "4afEgFZdll3FyRpvW/CDooe4Bppjd4aGn/Sr/o9nOzECQQD4QKLwZssuclji" +
+            "nD/8gU1CqGMMnGNogTMpHm1269HUOE7r1y3MuapUqSWsVhpuEQ8P/Tko0haJ" +
+            "jeZn2eWTbZu/AkEA591snui8FMeGvkRgvyMFNvXZWDEjsh+N74XEL1lykTgZ" +
+            "FQJ+cmThnrdM/8yj1dKkdASYrk5kFJ4PVE6CzDI43QJAFS22eNncJZc9u/9m" +
+            "eg0x4SjqYk4JMQYsripZXlbZ7Mfs+7O8xYVlYZmYjC5ATPmJlmyc7r2VjKCd" +
+            "cmilbEFikwJBAMh7yf8BaBdjitubzjeW9VxXaa37F01eQWD5PfBfHFP6uJ1V" +
+            "AbayCfAtuHN6I7OwJih3DPmyqJC3NrQECs67IjUCQAb4TfVE/2G1s66SGnb4" +
+            "no34BspoV/i4f0uLhJap84bTHcF/ZRSXCmQOCRGdSvQkXHeNPI5Lus6lOHuU" +
+            "vUDbQC8=");
+
     public NewEnvelopedDataTest()
     {
     }
@@ -208,6 +241,7 @@
             _reciDN2  = "CN=Fred, OU=Sales, O=Bouncy Castle, C=AU";
             _reciKP   = CMSTestUtil.makeKeyPair();
             _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+            _reciCertOaep = CMSTestUtil.makeOaepCertificate(_reciKP, _reciDN, _signKP, _signDN);
 
             _origEcKP = CMSTestUtil.makeEcDsaKeyPair();
             _reciEcKP = CMSTestUtil.makeEcDsaKeyPair();
@@ -291,7 +325,7 @@
         CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
 
         edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
-        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCert.getExtensionValue(X509Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), _reciCert.getPublicKey()).setProvider(BC));
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCert.getExtensionValue(Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), _reciCert.getPublicKey()).setProvider(BC));
 
         CMSEnvelopedData ed = edGen.generate(
                                 new CMSProcessableByteArray(data),
@@ -329,6 +363,179 @@
         assertTrue(collection.iterator().next() instanceof RecipientInformation);
     }
 
+    public void testKeyTransOAEPDefault()
+        throws Exception
+    {
+        byte[]          data     = "WallaWallaWashington".getBytes();
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+        JcaAlgorithmParametersConverter  paramsConverter = new JcaAlgorithmParametersConverter();
+
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert, paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, OAEPParameterSpec.DEFAULT)).setProvider(BC));
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCert.getExtensionValue(Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, OAEPParameterSpec.DEFAULT), _reciCert.getPublicKey()).setProvider(BC));
+
+        CMSEnvelopedData ed = edGen.generate(
+                                new CMSProcessableByteArray(data),
+                                new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider(BC).build());
+
+        RecipientInformationStore  recipients = ed.getRecipientInfos();
+
+
+        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
+
+        Collection  c = recipients.getRecipients();
+
+        assertEquals(2, c.size());
+
+        Iterator    it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation   recipient = (RecipientInformation)it.next();
+
+            assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, recipient.getKeyEncryptionAlgorithm().getAlgorithm());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
+
+            assertEquals(true, Arrays.equals(data, recData));
+        }
+
+        RecipientId id = new JceKeyTransRecipientId(_reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 2)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testKeyTransOAEPSHA1()
+            throws Exception
+    {
+        doTestKeyTransOAEPDefaultNamed("SHA-1");
+    }
+
+    public void testKeyTransOAEPSHA224()
+            throws Exception
+    {
+        doTestKeyTransOAEPDefaultNamed("SHA-224");
+    }
+
+    public void testKeyTransOAEPSHA256()
+            throws Exception
+    {
+        doTestKeyTransOAEPDefaultNamed("SHA-256");
+    }
+
+    public void testKeyTransOAEPSHA1AndSHA256()
+            throws Exception
+    {
+        doTestKeyTransOAEPDefaultNamed("SHA-1", "SHA-256");
+    }
+
+    private void doTestKeyTransOAEPDefaultNamed(String digest)
+        throws Exception
+    {
+        doTestKeyTransOAEPDefaultNamed(digest, digest);
+    }
+
+    private void doTestKeyTransOAEPDefaultNamed(String digest, String mgfDigest)
+        throws Exception
+    {
+        byte[]          data     = "WallaWallaWashington".getBytes();
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+        JcaAlgorithmParametersConverter  paramsConverter = new JcaAlgorithmParametersConverter();
+
+        OAEPParameterSpec oaepSpec = new OAEPParameterSpec(digest, "MGF1", new MGF1ParameterSpec(mgfDigest), new PSource.PSpecified(new byte[]{1, 2, 3, 4, 5}));
+        AlgorithmIdentifier oaepAlgId = paramsConverter.getAlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, oaepSpec);
+
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert, oaepAlgId).setProvider(BC));
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCert.getExtensionValue(Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), oaepAlgId, _reciCert.getPublicKey()).setProvider(BC));
+
+        CMSEnvelopedData ed = edGen.generate(
+                                new CMSProcessableByteArray(data),
+                                new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider(BC).build());
+
+        RecipientInformationStore  recipients = ed.getRecipientInfos();
+
+
+        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
+
+        Collection  c = recipients.getRecipients();
+
+        assertEquals(2, c.size());
+
+        Iterator    it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation   recipient = (RecipientInformation)it.next();
+
+            assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, recipient.getKeyEncryptionAlgorithm().getAlgorithm());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
+
+            assertEquals(true, Arrays.equals(data, recData));
+        }
+
+        RecipientId id = new JceKeyTransRecipientId(_reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 2)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testKeyTransOAEPInCert()
+         throws Exception
+     {
+         byte[]          data     = "WallaWallaWashington".getBytes();
+
+         CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+         edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCertOaep).setProvider(BC));
+         edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCertOaep.getExtensionValue(Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), _reciCertOaep.getPublicKey()).setProvider(BC));
+
+         CMSEnvelopedData ed = edGen.generate(
+                                 new CMSProcessableByteArray(data),
+                                 new JceCMSContentEncryptorBuilder(CMSAlgorithm.DES_EDE3_CBC).setProvider(BC).build());
+
+         RecipientInformationStore  recipients = ed.getRecipientInfos();
+
+
+         assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
+
+         Collection  c = recipients.getRecipients();
+
+         assertEquals(2, c.size());
+
+         Iterator    it = c.iterator();
+
+         while (it.hasNext())
+         {
+             RecipientInformation   recipient = (RecipientInformation)it.next();
+
+             assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, recipient.getKeyEncryptionAlgorithm().getAlgorithm());
+
+             byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
+
+             assertEquals(true, Arrays.equals(data, recData));
+         }
+
+         RecipientId id = new JceKeyTransRecipientId(_reciCertOaep);
+
+         Collection collection = recipients.getRecipients(id);
+         if (collection.size() != 2)
+         {
+             fail("recipients not matched using general recipient ID.");
+         }
+         assertTrue(collection.iterator().next() instanceof RecipientInformation);
+     }
+
     public void testKeyTransWithAlgMapping()
         throws Exception
     {
@@ -385,7 +592,7 @@
         edGen.setOriginatorInfo(new OriginatorInfoGenerator(origCert).generate());
 
         edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
-        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCert.getExtensionValue(X509Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), _reciCert.getPublicKey()).setProvider(BC));
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(ASN1OctetString.getInstance(ASN1OctetString.getInstance(_reciCert.getExtensionValue(Extension.subjectKeyIdentifier.getId())).getOctets()).getOctets(), _reciCert.getPublicKey()).setProvider(BC));
 
         CMSEnvelopedData ed = edGen.generate(
                                 new CMSProcessableByteArray(data),
@@ -425,6 +632,42 @@
         assertTrue(collection.iterator().next() instanceof RecipientInformation);
     }
 
+    public void testKeyTransRC2bit40()
+        throws Exception
+    {
+        byte[]          data     = "WallaWallaBouncyCastle".getBytes();
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.RC2_CBC, 40).setProvider(BC).build());
+
+        RecipientInformationStore  recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getContentEncryptionAlgorithm().getAlgorithm(), CMSAlgorithm.RC2_CBC);
+
+        RC2CBCParameter rc2P = RC2CBCParameter.getInstance(ed.getContentEncryptionAlgorithm().getParameters());
+        assertEquals(160, rc2P.getRC2ParameterVersion().intValue());
+
+        Collection  c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator    it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation   recipient = (RecipientInformation)it.next();
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
+
+            assertEquals(true, Arrays.equals(data, recData));
+        }
+    }
+
     public void testKeyTransRC4()
         throws Exception
     {
@@ -594,6 +837,44 @@
         }
     }
 
+    public void testKeyTransDESEDE3Short()
+        throws Exception
+    {
+        byte[]          data     = new byte[] { 0, 1, 2, 3 };
+        KeyFactory      kf = KeyFactory.getInstance("RSA", BC);
+        PrivateKey      kPriv = kf.generatePrivate(new PKCS8EncodedKeySpec(tooShort3DESKey));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(tooShort3DES);
+
+        RecipientInformationStore  recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
+
+        Collection  c = recipients.getRecipients();
+        Iterator    it = c.iterator();
+
+        if (it.hasNext())
+        {
+            RecipientInformation   recipient = (RecipientInformation)it.next();
+            try
+            {
+                byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(kPriv).setKeySizeValidation(true).setProvider(BC));
+                fail("invalid 3DES-EDE key not picked up");
+            }
+            catch (CMSException e)
+            {
+                assertEquals("Expected key size for algorithm OID not found in recipient.", e.getMessage());
+            }
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(kPriv).setKeySizeValidation(false).setProvider(BC));
+            assertEquals(true, Arrays.equals(data, recData));
+        }
+        else
+        {
+            fail("no recipient found");
+        }
+    }
+
     public void testKeyTransDESEDE3Light()
         throws Exception
     {
@@ -609,8 +890,7 @@
 
         RecipientInformationStore  recipients = ed.getRecipientInfos();
 
-        assertEquals(ed.getEncryptionAlgOID(),
-                                   CMSEnvelopedDataGenerator.DES_EDE3_CBC);
+        assertEquals(ed.getEncryptionAlgOID(), CMSEnvelopedDataGenerator.DES_EDE3_CBC);
 
         Collection  c = recipients.getRecipients();
         Iterator    it = c.iterator();
@@ -619,7 +899,7 @@
         {
             RecipientInformation   recipient = (RecipientInformation)it.next();
 
-            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setKeySizeValidation(true).setProvider(BC));
             assertEquals(true, Arrays.equals(data, recData));
         }
         else
@@ -725,7 +1005,7 @@
 
             assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
 
-            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setProvider(BC));
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()).setKeySizeValidation(true).setProvider(BC));
 
             assertEquals(true, Arrays.equals(data, recData));
         }
@@ -765,12 +1045,12 @@
     public void testDESKEK()
         throws Exception
     {
-        tryKekAlgorithm(CMSTestUtil.makeDesede192Key(), new DERObjectIdentifier("1.2.840.113549.1.9.16.3.6"));
+        tryKekAlgorithm(CMSTestUtil.makeDesede192Key(), new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6"));
     }
     public void testRC2128KEK()
         throws Exception
     {
-        tryKekAlgorithm(CMSTestUtil.makeRC2128Key(), new DERObjectIdentifier("1.2.840.113549.1.9.16.3.7"));
+        tryKekAlgorithm(CMSTestUtil.makeRC2128Key(), new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7"));
     }
 
     public void testAES128KEK()
@@ -815,7 +1095,7 @@
         tryKekAlgorithm(CMSTestUtil.makeCamelliaKey(256), NTTObjectIdentifiers.id_camellia256_wrap);
     }
 
-    private void tryKekAlgorithm(SecretKey kek, DERObjectIdentifier algOid)
+    private void tryKekAlgorithm(SecretKey kek, ASN1ObjectIdentifier algOid)
         throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
     {
         byte[]    data = "WallaWallaWashington".getBytes();
@@ -842,7 +1122,7 @@
 
             assertEquals(algOid.getId(), recipient.getKeyEncryptionAlgOID());
 
-            byte[] recData = recipient.getContent(new JceKEKEnvelopedRecipient(kek).setProvider(BC));
+            byte[] recData = recipient.getContent(new JceKEKEnvelopedRecipient(kek).setKeySizeValidation(true).setProvider(BC));
 
             assertTrue(Arrays.equals(data, recData));
         }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataStreamTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataStreamTest.java
index 9d9e645..8a92cae 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataStreamTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataStreamTest.java
@@ -33,7 +33,6 @@
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaCRLStore;
 import org.bouncycastle.cert.jcajce.JcaCertStore;
-import org.bouncycastle.cert.jcajce.JcaX509AttributeCertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CRLHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cert.ocsp.OCSPResp;
@@ -957,7 +956,7 @@
 
         gen.addCertificates(certs);
 
-        X509AttributeCertificateHolder attrCert = new JcaX509AttributeCertificateHolder(CMSTestUtil.getAttributeCertificate());
+        X509AttributeCertificateHolder attrCert = CMSTestUtil.getAttributeCertificate();
 
         Store store = new CollectionStore(Collections.singleton(attrCert));
 
@@ -1283,6 +1282,25 @@
         assertEquals(new JcaX509CertificateHolder(_origCert), it.next());
     }
 
+    public void testCertsOnly()
+        throws Exception
+    {
+        List certList = new ArrayList();
+        certList.add(_origCert);
+        certList.add(_signCert);
+    
+        Store certs = new JcaCertStore(certList);
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
+        gen.addCertificates(certs);
+
+        gen.open(bOut).close();
+
+        checkSigParseable(bOut.toByteArray());
+    }
+
     public static Test suite()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
index 9317b18..7df2c13 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
@@ -6,7 +6,6 @@
 import java.security.KeyPair;
 import java.security.MessageDigest;
 import java.security.Security;
-import java.security.cert.CertificateException;
 import java.security.cert.X509CRL;
 import java.security.cert.X509Certificate;
 import java.security.spec.PKCS8EncodedKeySpec;
@@ -39,7 +38,6 @@
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaCRLStore;
 import org.bouncycastle.cert.jcajce.JcaCertStore;
-import org.bouncycastle.cert.jcajce.JcaX509AttributeCertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CRLHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cert.ocsp.OCSPResp;
@@ -57,6 +55,7 @@
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.SignerInformationStore;
 import org.bouncycastle.cms.SignerInformationVerifier;
+import org.bouncycastle.cms.SignerInformationVerifierProvider;
 import org.bouncycastle.cms.bc.BcRSASignerInfoVerifierBuilder;
 import org.bouncycastle.cms.jcajce.JcaSignerId;
 import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
@@ -64,7 +63,6 @@
 import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.util.PrivateKeyFactory;
-import org.bouncycastle.cms.SignerInformationVerifierProvider;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
@@ -1663,7 +1661,7 @@
 
         gen.addCertificates(certs);
 
-        X509AttributeCertificateHolder attrCert = new JcaX509AttributeCertificateHolder(CMSTestUtil.getAttributeCertificate());
+        X509AttributeCertificateHolder attrCert = CMSTestUtil.getAttributeCertificate();
         List attrList = new ArrayList();
 
         attrList.add(new X509AttributeCertificateHolder(attrCert.getEncoded()));
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/NullProviderTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/NullProviderTest.java
index 4cfc498..a97b21c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NullProviderTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NullProviderTest.java
@@ -12,11 +12,8 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
-import java.security.Provider;
 import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.cert.CertStore;
-import java.security.cert.CollectionCertStoreParameters;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -29,23 +26,35 @@
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cms.CMSEnvelopedData;
 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
-import org.bouncycastle.cms.CMSProcessable;
 import org.bouncycastle.cms.CMSProcessableByteArray;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.cms.CMSSignedDataGenerator;
 import org.bouncycastle.cms.CMSSignedDataParser;
 import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
+import org.bouncycastle.cms.CMSTypedData;
 import org.bouncycastle.cms.CMSTypedStream;
 import org.bouncycastle.cms.RecipientInformation;
 import org.bouncycastle.cms.RecipientInformationStore;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
 import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.CollectionStore;
+import org.bouncycastle.util.Store;
 import org.bouncycastle.x509.X509V3CertificateGenerator;
 
 public class NullProviderTest
@@ -75,27 +84,26 @@
         throws Exception
     {
         List certList = new ArrayList();
-        CMSProcessable msg = new CMSProcessableByteArray(TEST_MESSAGE.getBytes());
+        CMSTypedData msg = new CMSProcessableByteArray(TEST_MESSAGE.getBytes());
 
-        certList.add(keyCert);
+        certList.add(new X509CertificateHolder(keyCert.getEncoded()));
 
-        CertStore certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList));
+        DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().build();
 
         CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
 
-        gen.addSigner(keyPair.getPrivate(), keyCert, CMSSignedDataGenerator.DIGEST_SHA1);
+        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("SHA1withRSA").build(keyPair.getPrivate()), keyCert));
 
-        gen.addCertificatesAndCRLs(certsAndCrls);
+        gen.addCertificates(new CollectionStore(certList));
 
-        CMSSignedData s = gen.generate(msg, true, (Provider)null);
+        CMSSignedData s = gen.generate(msg, true);
 
         ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
         ASN1InputStream aIn = new ASN1InputStream(bIn);
 
         s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
 
-        certsAndCrls = s.getCertificatesAndCRLs("Collection", (String)null); // make sure String works as well
+        Store certsAndCrls = s.getCertificates();
 
         SignerInformationStore signers = s.getSignerInfos();
         Collection c = signers.getSigners();
@@ -103,13 +111,12 @@
 
         while (it.hasNext())
         {
-            SignerInformation signer = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            SignerInformation     signer = (SignerInformation)it.next();
+            Collection            certCollection = certsAndCrls.getMatches(signer.getSID());
+            Iterator              certIt = certCollection.iterator();
+            X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
 
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertEquals(true, signer.verify(cert, (Provider)null));
+            assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)));
         }
     }
 
@@ -119,16 +126,15 @@
         List                  certList = new ArrayList();
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
-        certList.add(keyCert);
+        certList.add(new X509CertificateHolder(keyCert.getEncoded()));
 
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList));
+        DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().build();
 
         CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
 
-        gen.addSigner(keyPair.getPrivate(), keyCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, (String)null);
+        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("SHA1withRSA").build(keyPair.getPrivate()), keyCert));
 
-        gen.addCertificatesAndCRLs(certsAndCrls);
+        gen.addCertificates(new CollectionStore(certList));
 
         OutputStream sigOut = gen.open(bOut);
 
@@ -136,7 +142,7 @@
 
         sigOut.close();
 
-        CMSSignedDataParser sp = new CMSSignedDataParser(
+        CMSSignedDataParser sp = new CMSSignedDataParser(digCalcProv,
                 new CMSTypedStream(new ByteArrayInputStream(TEST_MESSAGE.getBytes())), bOut.toByteArray());
 
         sp.getSignedContent().drain();
@@ -147,7 +153,7 @@
         MessageDigest md = MessageDigest.getInstance("SHA1");
 
         byte[]                  contentDigest = md.digest(TEST_MESSAGE.getBytes());
-        CertStore               certStore = sp.getCertificatesAndCRLs("Collection", (String)null);
+        Store                   certStore = sp.getCertificates();
         SignerInformationStore  signers = sp.getSignerInfos();
 
         Collection              c = signers.getSigners();
@@ -156,12 +162,12 @@
         while (it.hasNext())
         {
             SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certStore.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            Collection          certCollection = certStore.getMatches(signer.getSID());
 
             Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
+            X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
 
-            assertEquals(true, signer.verify(cert, (Provider)null));
+            assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().build(cert)));
 
             if (contentDigest != null)
             {
@@ -201,15 +207,14 @@
 
         CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
 
-        edGen.addKeyTransRecipient(keyCert);
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(keyCert));
 
         CMSEnvelopedData ed = edGen.generate(
-                                new CMSProcessableByteArray(data),
-                                algorithm, (String)null);
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(algorithm)).build());
 
         RecipientInformationStore recipients = ed.getRecipientInfos();
 
-
         assertEquals(ed.getEncryptionAlgOID(), algorithm);
 
         Collection  c = recipients.getRecipients();
@@ -224,7 +229,7 @@
 
             assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
 
-            byte[] recData = recipient.getContent(keyPair.getPrivate(), (String)null);
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(keyPair.getPrivate()));
 
             assertEquals(true, Arrays.equals(data, recData));
         }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/Rfc4134Test.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/Rfc4134Test.java
index f36b7b7..2f59702 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/Rfc4134Test.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/Rfc4134Test.java
@@ -10,7 +10,6 @@
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.Security;
-import java.security.cert.CertStore;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.DSAParams;
@@ -33,6 +32,8 @@
 import org.bouncycastle.asn1.cms.CMSAttributes;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cms.CMSEnvelopedData;
 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
 import org.bouncycastle.cms.CMSEnvelopedDataParser;
@@ -45,8 +46,14 @@
 import org.bouncycastle.cms.RecipientInformationStore;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
 import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.Store;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.io.Streams;
 
@@ -60,6 +67,19 @@
     private static byte[] sha1 = Hex.decode("406aec085279ba6e16022d9e0629c0229687dd48");
 
     private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
+    private static final DigestCalculatorProvider digCalcProv;
+
+    static
+    {
+        try
+        {
+            digCalcProv =  new JcaDigestCalculatorProviderBuilder().build();
+        }
+        catch (OperatorCreationException e)
+        {
+            throw new IllegalStateException("can't create default provider!!!");
+        }
+    }
 
     public Rfc4134Test(String name)
     {
@@ -87,7 +107,7 @@
 
         verifySignatures(signedData);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(data);
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv, data);
 
         verifySignatures(parser);
     }
@@ -100,7 +120,7 @@
 
         verifySignatures(signedData);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(data);
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv, data);
 
         verifySignatures(parser);
     }
@@ -113,7 +133,7 @@
 
         verifySignatures(signedData, sha1);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv,
                 new CMSTypedStream(new ByteArrayInputStream(exContent)),
                 data);
 
@@ -131,7 +151,7 @@
 
         verifySignerInfo4_4(getFirstSignerInfo(signedData.getSignerInfos()), counterSigCert);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(data);
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv, data);
 
         verifySignatures(parser);
 
@@ -146,7 +166,7 @@
 
         verifySignatures(signedData);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(data);
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv, data);
 
         verifySignatures(parser);
     }
@@ -159,7 +179,7 @@
 
         verifySignatures(signedData);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(data);
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv, data);
 
         verifySignatures(parser);
     }
@@ -172,7 +192,7 @@
 
         verifySignatures(signedData);
 
-        CMSSignedDataParser parser = new CMSSignedDataParser(data);
+        CMSSignedDataParser parser = new CMSSignedDataParser(digCalcProv, data);
 
         verifySignatures(parser);
     }
@@ -260,7 +280,7 @@
     {
         assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
 
-        byte[] recData = recipient.getContent(privKey, BC);
+        byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
 
         assertEquals(true, Arrays.equals(exContent, recData));
     }
@@ -286,7 +306,7 @@
         CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
         X509Certificate    cert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(certificate));
 
-        assertTrue(csi.verify(cert, BC));
+        assertTrue(csi.verify(new JcaSignerInfoVerifierBuilder(digCalcProv).setProvider(BC).build(cert)));
     }
 
     private void verifyContentHint(SignerInformation signInfo)
@@ -308,7 +328,7 @@
     private void verifySignatures(CMSSignedData s, byte[] contentDigest)
         throws Exception
     {
-        CertStore               certStore = s.getCertificatesAndCRLs("Collection", BC);
+        Store               certStore = s.getCertificates();
         SignerInformationStore  signers = s.getSignerInfos();
 
         Collection              c = signers.getSigners();
@@ -317,10 +337,10 @@
         while (it.hasNext())
         {
             SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certStore.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            Collection          certCollection = certStore.getMatches(signer.getSID());
 
             Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
+            X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
 
             verifySigner(signer, cert);
 
@@ -329,12 +349,6 @@
                 assertTrue(MessageDigest.isEqual(contentDigest, signer.getContentDigest()));
             }
         }
-
-        Collection certColl = certStore.getCertificates(null);
-        Collection crlColl = certStore.getCRLs(null);
-
-        assertEquals(certColl.size(), s.getCertificates("Collection", BC).getMatches(null).size());
-        assertEquals(crlColl.size(), s.getCRLs("Collection", BC).getMatches(null).size());
     }
 
     private void verifySignatures(CMSSignedData s)
@@ -352,7 +366,7 @@
             sc.drain();
         }
         
-        CertStore               certs = sp.getCertificatesAndCRLs("Collection", BC);
+        Store certs = sp.getCertificates();
         SignerInformationStore  signers = sp.getSignerInfos();
 
         Collection              c = signers.getSigners();
@@ -361,34 +375,35 @@
         while (it.hasNext())
         {
             SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            Collection          certCollection = certs.getMatches(signer.getSID());
 
             Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
+            X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
 
             verifySigner(signer, cert);
         }
     }
 
-    private void verifySigner(SignerInformation signer, X509Certificate cert)
+    private void verifySigner(SignerInformation signer, X509CertificateHolder certHolder)
         throws Exception
     {
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate(certHolder);
         if (cert.getPublicKey() instanceof DSAPublicKey)
         {
             DSAPublicKey key = (DSAPublicKey)cert.getPublicKey();
 
             if (key.getParams() == null)
             {
-                assertEquals(true, signer.verify(getInheritedKey(key), BC));
+                assertEquals(true, signer.verify(new JcaSignerInfoVerifierBuilder(digCalcProv).setProvider(BC).build(getInheritedKey(key))));
             }
             else
             {
-                assertEquals(true, signer.verify(cert, BC));
+                assertEquals(true, signer.verify(new JcaSignerInfoVerifierBuilder(digCalcProv).setProvider(BC).build(cert)));
             }
         }
         else
         {
-            assertEquals(true, signer.verify(cert, BC));
+            assertEquals(true, signer.verify(new JcaSignerInfoVerifierBuilder(digCalcProv).setProvider(BC).build(cert)));
         }
     }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/SignedDataStreamTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/SignedDataStreamTest.java
deleted file mode 100644
index 39b50da..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/SignedDataStreamTest.java
+++ /dev/null
@@ -1,1158 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.OutputStream;
-import java.security.InvalidKeyException;
-import java.security.KeyPair;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertStore;
-import java.security.cert.CollectionCertStoreParameters;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Hashtable;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.ASN1OctetString;
-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.cms.CMSAttributeTableGenerator;
-import org.bouncycastle.cms.CMSProcessable;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.CMSSignedDataParser;
-import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
-import org.bouncycastle.cms.CMSSignedGenerator;
-import org.bouncycastle.cms.CMSTypedStream;
-import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
-import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.x509.X509AttributeCertificate;
-import org.bouncycastle.x509.X509CollectionStoreParameters;
-import org.bouncycastle.x509.X509Store;
-
-public class SignedDataStreamTest
-    extends TestCase
-{
-    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
-
-    private static final String TEST_MESSAGE = "Hello World!";
-    private static String          _signDN;
-    private static KeyPair         _signKP;  
-    private static X509Certificate _signCert;
-
-    private static String          _origDN;
-    private static KeyPair         _origKP;
-    private static X509Certificate _origCert;
-
-    private static String          _reciDN;
-    private static KeyPair         _reciKP;
-    private static X509Certificate _reciCert;
-    
-    private static KeyPair         _origDsaKP;
-    private static X509Certificate _origDsaCert;
-
-    private static X509CRL         _signCrl;
-    private static X509CRL         _origCrl;
-
-    private static boolean         _initialised = false;
-
-    private static final JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
-
-    public SignedDataStreamTest(String name) 
-    {
-        super(name);
-    }
-    
-    private static void init()
-        throws Exception
-    {
-        if (!_initialised)
-        {
-            _initialised = true;
-            
-            _signDN   = "O=Bouncy Castle, C=AU";
-            _signKP   = CMSTestUtil.makeKeyPair();  
-            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
-    
-            _origDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
-            _origKP   = CMSTestUtil.makeKeyPair();
-            _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _signKP, _signDN);
-    
-            _origDsaKP   = CMSTestUtil.makeDsaKeyPair();
-            _origDsaCert = CMSTestUtil.makeCertificate(_origDsaKP, _origDN, _signKP, _signDN);
-            
-            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciKP   = CMSTestUtil.makeKeyPair();
-            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
-
-            _signCrl  = CMSTestUtil.makeCrl(_signKP);
-            _origCrl  = CMSTestUtil.makeCrl(_origKP);
-        }
-    }
-    
-    private void verifySignatures(CMSSignedDataParser sp, byte[] contentDigest) 
-        throws Exception
-    {
-        CertStore               certStore = sp.getCertificatesAndCRLs("Collection", BC);
-        SignerInformationStore  signers = sp.getSignerInfos();
-        
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-    
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certStore.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-    
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-    
-            assertEquals(true, signer.verify(cert, BC));
-            
-            if (contentDigest != null)
-            {
-                assertTrue(MessageDigest.isEqual(contentDigest, signer.getContentDigest()));
-            }
-        }
-
-        Collection certColl = certStore.getCertificates(null);
-        Collection crlColl = certStore.getCRLs(null);
-
-        assertEquals(certColl.size(), sp.getCertificates("Collection", BC).getMatches(null).size());
-        assertEquals(crlColl.size(), sp.getCRLs("Collection", BC).getMatches(null).size());
-    }
-    
-    private void verifySignatures(CMSSignedDataParser sp) 
-        throws Exception
-    {
-        verifySignatures(sp, null);
-    }
-
-    private void verifyEncodedData(ByteArrayOutputStream bOut)
-        throws Exception
-    {
-        CMSSignedDataParser sp;
-        sp = new CMSSignedDataParser(bOut.toByteArray());
-    
-        sp.getSignedContent().drain();
-        
-        verifySignatures(sp);
-        
-        sp.close();
-    }
-
-    private void checkSigParseable(byte[] sig)
-        throws Exception
-    {
-        CMSSignedDataParser sp = new CMSSignedDataParser(sig);
-        sp.getVersion();
-        CMSTypedStream sc = sp.getSignedContent();
-        if (sc != null)
-        {
-            sc.drain();
-        }
-        sp.getCertificatesAndCRLs("Collection", BC);
-        sp.getSignerInfos();
-        sp.close();
-    }
-
-    public void testEarlyInvalidKeyException() throws Exception
-    {
-        try
-        {
-            CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-            gen.addSigner( _origKP.getPrivate(), _origCert,
-                "DSA", // DOESN'T MATCH KEY ALG
-                CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-            fail("Expected InvalidKeyException in addSigner");
-        }
-        catch (InvalidKeyException e)
-        {
-            // Ignore
-        }
-    }
-
-    public void testEarlyNoSuchAlgorithmException() throws Exception
-    {
-        try
-        {
-            CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-            gen.addSigner( _origKP.getPrivate(), _origCert,
-                CMSSignedDataStreamGenerator.DIGEST_SHA1, // BAD OID!
-                CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-            fail("Expected NoSuchAlgorithmException in addSigner");
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            // Ignore
-        }
-    }
-
-    public void testSha1EncapsulatedSignature()
-        throws Exception
-    {
-        byte[]  encapSigData = Base64.decode(
-                  "MIAGCSqGSIb3DQEHAqCAMIACAQExCzAJBgUrDgMCGgUAMIAGCSqGSIb3DQEH"
-                + "AaCAJIAEDEhlbGxvIFdvcmxkIQAAAAAAAKCCBGIwggINMIIBdqADAgECAgEF"
-                + "MA0GCSqGSIb3DQEBBAUAMCUxFjAUBgNVBAoTDUJvdW5jeSBDYXN0bGUxCzAJ"
-                + "BgNVBAYTAkFVMB4XDTA1MDgwNzA2MjU1OVoXDTA1MTExNTA2MjU1OVowJTEW"
-                + "MBQGA1UEChMNQm91bmN5IENhc3RsZTELMAkGA1UEBhMCQVUwgZ8wDQYJKoZI"
-                + "hvcNAQEBBQADgY0AMIGJAoGBAI1fZGgH9wgC3QiK6yluH6DlLDkXkxYYL+Qf"
-                + "nVRszJVYl0LIxZdpb7WEbVpO8fwtEgFtoDsOdxyqh3dTBv+L7NVD/v46kdPt"
-                + "xVkSNHRbutJVY8Xn4/TC/CDngqtbpbniMO8n0GiB6vs94gBT20M34j96O2IF"
-                + "73feNHP+x8PkJ+dNAgMBAAGjTTBLMB0GA1UdDgQWBBQ3XUfEE6+D+t+LIJgK"
-                + "ESSUE58eyzAfBgNVHSMEGDAWgBQ3XUfEE6+D+t+LIJgKESSUE58eyzAJBgNV"
-                + "HRMEAjAAMA0GCSqGSIb3DQEBBAUAA4GBAFK3r1stYOeXYJOlOyNGDTWEhZ+a"
-                + "OYdFeFaS6c+InjotHuFLAy+QsS8PslE48zYNFEqYygGfLhZDLlSnJ/LAUTqF"
-                + "01vlp+Bgn/JYiJazwi5WiiOTf7Th6eNjHFKXS3hfSGPNPIOjvicAp3ce3ehs"
-                + "uK0MxgLAaxievzhFfJcGSUMDMIICTTCCAbagAwIBAgIBBzANBgkqhkiG9w0B"
-                + "AQQFADAlMRYwFAYDVQQKEw1Cb3VuY3kgQ2FzdGxlMQswCQYDVQQGEwJBVTAe"
-                + "Fw0wNTA4MDcwNjI1NTlaFw0wNTExMTUwNjI1NTlaMGUxGDAWBgNVBAMTD0Vy"
-                + "aWMgSC4gRWNoaWRuYTEkMCIGCSqGSIb3DQEJARYVZXJpY0Bib3VuY3ljYXN0"
-                + "bGUub3JnMRYwFAYDVQQKEw1Cb3VuY3kgQ2FzdGxlMQswCQYDVQQGEwJBVTCB"
-                + "nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAgHCJyfwV6/V3kqSu2SOU2E/K"
-                + "I+N0XohCMUaxPLLNtNBZ3ijxwaV6JGFz7siTgZD/OGfzir/eZimkt+L1iXQn"
-                + "OAB+ZChivKvHtX+dFFC7Vq+E4Uy0Ftqc/wrGxE6DHb5BR0hprKH8wlDS8wSP"
-                + "zxovgk4nH0ffUZOoDSuUgjh3gG8CAwEAAaNNMEswHQYDVR0OBBYEFLfY/4EG"
-                + "mYrvJa7Cky+K9BJ7YmERMB8GA1UdIwQYMBaAFDddR8QTr4P634sgmAoRJJQT"
-                + "nx7LMAkGA1UdEwQCMAAwDQYJKoZIhvcNAQEEBQADgYEADIOmpMd6UHdMjkyc"
-                + "mIE1yiwfClCsGhCK9FigTg6U1G2FmkBwJIMWBlkeH15uvepsAncsgK+Cn3Zr"
-                + "dZMb022mwtTJDtcaOM+SNeuCnjdowZ4i71Hf68siPm6sMlZkhz49rA0Yidoo"
-                + "WuzYOO+dggzwDsMldSsvsDo/ARyCGOulDOAxggEvMIIBKwIBATAqMCUxFjAU"
-                + "BgNVBAoTDUJvdW5jeSBDYXN0bGUxCzAJBgNVBAYTAkFVAgEHMAkGBSsOAwIa"
-                + "BQCgXTAYBgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEP"
-                + "Fw0wNTA4MDcwNjI1NTlaMCMGCSqGSIb3DQEJBDEWBBQu973mCM5UBOl9XwQv"
-                + "lfifHCMocTANBgkqhkiG9w0BAQEFAASBgGxnBl2qozYKLgZ0ygqSFgWcRGl1"
-                + "LgNuE587LtO+EKkgoc3aFqEdjXlAyP8K7naRsvWnFrsB6pUpnrgI9Z8ZSKv8"
-                + "98IlpsSSJ0jBlEb4gzzavwcBpYbr2ryOtDcF+kYmKIpScglyyoLzm+KPXOoT"
-                + "n7MsJMoKN3Kd2Vzh6s10PFgeAAAAAAAA");
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(encapSigData);
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-    }
-    
-    public void testSHA1WithRSANoAttributes()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray(TEST_MESSAGE.getBytes());
-    
-        certList.add(_origCert);
-        certList.add(_signCert);
-    
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-    
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-    
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    
-        gen.addCertificatesAndCRLs(certs);
-    
-        CMSSignedData s = gen.generate(CMSSignedDataGenerator.DATA, msg, false, BC, false);
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(
-                new CMSTypedStream(new ByteArrayInputStream(TEST_MESSAGE.getBytes())), s.getEncoded());
-        
-        sp.getSignedContent().drain();
-        
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance("SHA1", BC);
-        
-        verifySignatures(sp, md.digest(TEST_MESSAGE.getBytes()));
-    }
-    
-    public void testDSANoAttributes()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray(TEST_MESSAGE.getBytes());
-    
-        certList.add(_origDsaCert);
-        certList.add(_signCert);
-    
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-    
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-    
-        gen.addSigner(_origDsaKP.getPrivate(), _origDsaCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    
-        gen.addCertificatesAndCRLs(certs);
-    
-        CMSSignedData s = gen.generate(CMSSignedDataGenerator.DATA, msg, false, BC, false);
-    
-        CMSSignedDataParser     sp = new CMSSignedDataParser(
-                new CMSTypedStream(new ByteArrayInputStream(TEST_MESSAGE.getBytes())), s.getEncoded());
-        
-        sp.getSignedContent().drain();
-        
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance("SHA1", BC);
-        
-        verifySignatures(sp, md.digest(TEST_MESSAGE.getBytes()));
-    }
-    
-    public void testSHA1WithRSA()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certList.add(_signCrl);
-        certList.add(_origCrl);
-
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-    
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-    
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-    
-        gen.addCertificatesAndCRLs(certsAndCrls);
-    
-        OutputStream sigOut = gen.open(bOut);
-    
-        sigOut.write(TEST_MESSAGE.getBytes());
-        
-        sigOut.close();
-
-        checkSigParseable(bOut.toByteArray());
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(
-                new CMSTypedStream(new ByteArrayInputStream(TEST_MESSAGE.getBytes())), bOut.toByteArray());
-    
-        sp.getSignedContent().drain();
-        
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance("SHA1", BC);
-        
-        verifySignatures(sp, md.digest(TEST_MESSAGE.getBytes()));
-        
-        //
-        // try using existing signer
-        //
-        gen = new CMSSignedDataStreamGenerator();
-    
-        gen.addSigners(sp.getSignerInfos());
-        
-        gen.addCertificatesAndCRLs(sp.getCertificatesAndCRLs("Collection", BC));
-        
-        bOut.reset();
-        
-        sigOut = gen.open(bOut, true);
-    
-        sigOut.write(TEST_MESSAGE.getBytes());
-        
-        sigOut.close();
-    
-        verifyEncodedData(bOut);
-
-        //
-        // look for the CRLs
-        //
-        Collection col = certsAndCrls.getCRLs(null);
-
-        assertEquals(2, col.size());
-        assertTrue(col.contains(_signCrl));
-        assertTrue(col.contains(_origCrl));
-    }
-
-    public void testSHA1WithRSANonData()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certList.add(_signCrl);
-        certList.add(_origCrl);
-
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                                                       new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certsAndCrls);
-
-        OutputStream sigOut = gen.open(bOut, "1.2.3.4", true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        CMSTypedStream stream = sp.getSignedContent();
-
-        assertEquals(new ASN1ObjectIdentifier("1.2.3.4"), stream.getContentType());
-
-        stream.drain();
-
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance("SHA1", BC);
-
-        verifySignatures(sp, md.digest(TEST_MESSAGE.getBytes()));
-    }
-
-    public void testSHA1AndMD5WithRSA()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        certList.add(_origCert);
-        certList.add(_signCert);
-    
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-    
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-    
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_MD5, BC);
-        
-        gen.addCertificatesAndCRLs(certs);
-    
-        OutputStream sigOut = gen.open(bOut);
-    
-        sigOut.write(TEST_MESSAGE.getBytes());
-        
-        sigOut.close();
-
-        checkSigParseable(bOut.toByteArray());
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(
-                new CMSTypedStream(new ByteArrayInputStream(TEST_MESSAGE.getBytes())), bOut.toByteArray());
-    
-        sp.getSignedContent().drain();
-        
-        verifySignatures(sp);
-    }
-    
-    public void testSHA1WithRSAEncapsulatedBufferedStream()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                               new CollectionCertStoreParameters(certList), BC);
-
-        //
-        // find unbuffered length
-        //
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            sigOut.write(i & 0xff);
-        }
-        
-        sigOut.close();
-        
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-        
-        verifySignatures(sp);
-        
-        int unbufferedLength = bOut.toByteArray().length;
-        
-        //
-        // find buffered length with buffered stream - should be equal
-        //
-        bOut = new ByteArrayOutputStream();
-
-        gen = new CMSSignedDataStreamGenerator();
-        
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        sigOut = gen.open(bOut, true);
-
-        BufferedOutputStream bfOut = new BufferedOutputStream(sigOut, 300);
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            bfOut.write(i & 0xff);
-        }
-        
-        bfOut.close();
-        
-        verifyEncodedData(bOut);
-        
-        assertTrue(bOut.toByteArray().length == unbufferedLength);
-    }
-
-    public void testSHA1WithRSAEncapsulatedBuffered()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        certList.add(_origCert);
-        certList.add(_signCert);
-    
-        CertStore           certs = CertStore.getInstance("Collection",
-                               new CollectionCertStoreParameters(certList), BC);
-    
-        //
-        // find unbuffered length
-        //
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-    
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-    
-        gen.addCertificatesAndCRLs(certs);
-    
-        OutputStream sigOut = gen.open(bOut, true);
-        
-        for (int i = 0; i != 2000; i++)
-        {
-            sigOut.write(i & 0xff);
-        }
-        
-        sigOut.close();
-        
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-    
-        sp.getSignedContent().drain();
-        
-        verifySignatures(sp);
-        
-        int unbufferedLength = bOut.toByteArray().length;
-        
-        //
-        // find buffered length - buffer size less than default
-        //
-        bOut = new ByteArrayOutputStream();
-    
-        gen = new CMSSignedDataStreamGenerator();
-        
-        gen.setBufferSize(300);
-        
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-    
-        gen.addCertificatesAndCRLs(certs);
-    
-        sigOut = gen.open(bOut, true);
-    
-        for (int i = 0; i != 2000; i++)
-        {
-            sigOut.write(i & 0xff);
-        }
-        
-        sigOut.close();
-        
-        verifyEncodedData(bOut);
-
-        assertTrue(bOut.toByteArray().length > unbufferedLength);
-    }
-    
-    public void testSHA1WithRSAEncapsulated()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-        
-        sigOut.close();
-        
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-        
-        verifySignatures(sp);
-        
-        byte[] contentDigest = (byte[])gen.getGeneratedDigests().get(CMSSignedGenerator.DIGEST_SHA1);
-
-        AttributeTable table = ((SignerInformation)sp.getSignerInfos().getSigners().iterator().next()).getSignedAttributes();
-        Attribute hash = table.get(CMSAttributes.messageDigest);
-
-        assertTrue(MessageDigest.isEqual(contentDigest, ((ASN1OctetString)hash.getAttrValues().getObjectAt(0)).getOctets()));
-
-        //
-        // try using existing signer
-        //
-        gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigners(sp.getSignerInfos());
-        
-        gen.addCertificatesAndCRLs(sp.getCertificatesAndCRLs("Collection", BC));
-        
-        bOut.reset();
-        
-        sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-        
-        sigOut.close();
-
-        CMSSignedData sd = new CMSSignedData(new CMSProcessableByteArray(TEST_MESSAGE.getBytes()), bOut.toByteArray());
-
-        assertEquals(1, sd.getSignerInfos().getSigners().size());
-
-        verifyEncodedData(bOut);
-    }
-
-    public void testSHA1WithRSAEncapsulatedSubjectKeyID()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), CMSTestUtil.createSubjectKeyId(_origCert.getPublicKey()).getKeyIdentifier(), CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-
-        byte[] contentDigest = (byte[])gen.getGeneratedDigests().get(CMSSignedGenerator.DIGEST_SHA1);
-
-        AttributeTable table = ((SignerInformation)sp.getSignerInfos().getSigners().iterator().next()).getSignedAttributes();
-        Attribute hash = table.get(CMSAttributes.messageDigest);
-
-        assertTrue(MessageDigest.isEqual(contentDigest, ((ASN1OctetString)hash.getAttrValues().getObjectAt(0)).getOctets()));
-
-        //
-        // try using existing signer
-        //
-        gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigners(sp.getSignerInfos());
-
-        gen.addCertificatesAndCRLs(sp.getCertificatesAndCRLs("Collection", BC));
-
-        bOut.reset();
-
-        sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedData sd = new CMSSignedData(new CMSProcessableByteArray(TEST_MESSAGE.getBytes()), bOut.toByteArray());
-
-        assertEquals(1, sd.getSignerInfos().getSigners().size());
-
-        verifyEncodedData(bOut);
-    }
-
-    public void testAttributeGenerators()
-        throws Exception
-    {
-        final ASN1ObjectIdentifier dummyOid1 = new ASN1ObjectIdentifier("1.2.3");
-        final ASN1ObjectIdentifier dummyOid2 = new ASN1ObjectIdentifier("1.2.3.4");
-        List                      certList = new ArrayList();
-        ByteArrayOutputStream     bOut = new ByteArrayOutputStream();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        CMSAttributeTableGenerator signedGen = new DefaultSignedAttributeTableGenerator()
-        {
-            public AttributeTable getAttributes(Map parameters)
-            {
-                Hashtable table = createStandardAttributeTable(parameters);
-
-                DEROctetString val = new DEROctetString((byte[])parameters.get(CMSAttributeTableGenerator.DIGEST));
-                Attribute attr = new Attribute(dummyOid1, new DERSet(val));
-
-                table.put(attr.getAttrType(), attr);
-
-                return new AttributeTable(table);
-            }
-        };
-
-        CMSAttributeTableGenerator unsignedGen = new CMSAttributeTableGenerator()
-        {
-            public AttributeTable getAttributes(Map parameters)
-            {
-                DEROctetString val = new DEROctetString((byte[])parameters.get(CMSAttributeTableGenerator.SIGNATURE));
-                Attribute attr = new Attribute(dummyOid2, new DERSet(val));
-
-                return new AttributeTable(new DERSet(attr));
-            }
-        };
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, signedGen, unsignedGen, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-
-        //
-        // check attributes
-        //
-        SignerInformationStore  signers = sp.getSignerInfos();
-
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            checkAttribute(signer.getContentDigest(), signer.getSignedAttributes().get(dummyOid1));
-            checkAttribute(signer.getSignature(), signer.getUnsignedAttributes().get(dummyOid2));
-        }
-    }
-
-    private void checkAttribute(byte[] expected, Attribute attr)
-    {
-        DEROctetString      value = (DEROctetString)attr.getAttrValues().getObjectAt(0);
-
-        assertEquals(new DEROctetString(expected), value);
-    }
-
-    public void testWithAttributeCertificate()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        X509AttributeCertificate attrCert = CMSTestUtil.getAttributeCertificate();
-
-        X509Store store = X509Store.getInstance("AttributeCertificate/Collection",
-                                    new X509CollectionStoreParameters(Collections.singleton(attrCert)), BC);
-
-        gen.addAttributeCertificates(store);
-
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedDataParser     sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        assertEquals(4, sp.getVersion());
-
-        store = sp.getAttributeCertificates("Collection", BC);
-
-        Collection coll = store.getMatches(null);
-
-        assertEquals(1, coll.size());
-
-        assertTrue(coll.contains(attrCert));
-    }
-
-    public void testSignerStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        byte[]                data = TEST_MESSAGE.getBytes();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, false);
-
-        sigOut.write(data);
-
-        sigOut.close();
-
-        checkSigParseable(bOut.toByteArray());
-
-        //
-        // create new Signer
-        //
-        ByteArrayInputStream  original = new ByteArrayInputStream(bOut.toByteArray());
-
-        bOut.reset();
-
-        gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA224, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        sigOut = gen.open(bOut);
-
-        sigOut.write(data);
-
-        sigOut.close();
-
-        checkSigParseable(bOut.toByteArray());
-
-        CMSSignedData sd = new CMSSignedData(bOut.toByteArray());
-
-        //
-        // replace signer
-        //
-        ByteArrayOutputStream newOut = new ByteArrayOutputStream();
-
-        CMSSignedDataParser.replaceSigners(original, sd.getSignerInfos(), newOut);
-
-        sd = new CMSSignedData(new CMSProcessableByteArray(data), newOut.toByteArray());
-        SignerInformation signer = (SignerInformation)sd.getSignerInfos().getSigners().iterator().next();
-
-        assertEquals(signer.getDigestAlgOID(), CMSSignedDataStreamGenerator.DIGEST_SHA224);
-
-        CMSSignedDataParser sp = new CMSSignedDataParser(new CMSTypedStream(new ByteArrayInputStream(data)), newOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-    }
-
-    public void testEncapsulatedSignerStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        //
-        // create new Signer
-        //
-        ByteArrayInputStream  original = new ByteArrayInputStream(bOut.toByteArray());
-
-        bOut.reset();
-
-        gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA224, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedData sd = new CMSSignedData(bOut.toByteArray());
-
-        //
-        // replace signer
-        //
-        ByteArrayOutputStream newOut = new ByteArrayOutputStream();
-
-        CMSSignedDataParser.replaceSigners(original, sd.getSignerInfos(), newOut);
-
-        sd = new CMSSignedData(newOut.toByteArray());
-        SignerInformation signer = (SignerInformation)sd.getSignerInfos().getSigners().iterator().next();
-
-        assertEquals(signer.getDigestAlgOID(), CMSSignedDataStreamGenerator.DIGEST_SHA224);
-
-        CMSSignedDataParser sp = new CMSSignedDataParser(newOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-    }
-
-    public void testCertStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        byte[]                data = TEST_MESSAGE.getBytes();
-
-        certList.add(_origDsaCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut);
-
-        sigOut.write(data);
-
-        sigOut.close();
-
-        checkSigParseable(bOut.toByteArray());
-
-        //
-        // create new certstore with the right certificates
-        //
-        certList = new ArrayList();
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        //
-        // replace certs
-        //
-        ByteArrayInputStream original = new ByteArrayInputStream(bOut.toByteArray());
-        ByteArrayOutputStream newOut = new ByteArrayOutputStream();
-
-        CMSSignedDataParser.replaceCertificatesAndCRLs(original, certs, newOut);
-
-        CMSSignedDataParser sp = new CMSSignedDataParser(new CMSTypedStream(new ByteArrayInputStream(data)), newOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-    }
-
-    public void testEncapsulatedCertStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        certList.add(_origDsaCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        //
-        // create new certstore with the right certificates
-        //
-        certList = new ArrayList();
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        //
-        // replace certs
-        //
-        ByteArrayInputStream original = new ByteArrayInputStream(bOut.toByteArray());
-        ByteArrayOutputStream newOut = new ByteArrayOutputStream();
-
-        CMSSignedDataParser.replaceCertificatesAndCRLs(original, certs, newOut);
-
-        CMSSignedDataParser sp = new CMSSignedDataParser(newOut.toByteArray());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-    }
-
-    public void testCertOrdering1()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedDataParser sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-        certs = sp.getCertificatesAndCRLs("Collection", BC);
-        Iterator it = certs.getCertificates(null).iterator();
-
-        assertEquals(_origCert, it.next());
-        assertEquals(_signCert, it.next());
-    }
-
-    public void testCertOrdering2()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-        certList.add(_signCert);
-        certList.add(_origCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, BC);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        OutputStream sigOut = gen.open(bOut, true);
-
-        sigOut.write(TEST_MESSAGE.getBytes());
-
-        sigOut.close();
-
-        CMSSignedDataParser sp = new CMSSignedDataParser(bOut.toByteArray());
-
-        sp.getSignedContent().drain();
-        certs = sp.getCertificatesAndCRLs("Collection", BC);
-        Iterator it = certs.getCertificates(null).iterator();
-
-        assertEquals(_signCert, it.next());
-        assertEquals(_origCert, it.next());
-    }
-
-    public static Test suite()
-        throws Exception
-    {
-        init();
-        
-        return new CMSTestSetup(new TestSuite(SignedDataStreamTest.class));
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/SignedDataTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/SignedDataTest.java
deleted file mode 100644
index 160669b..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/SignedDataTest.java
+++ /dev/null
@@ -1,1573 +0,0 @@
-package org.bouncycastle.cms.test;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.cert.CertStore;
-import java.security.cert.CollectionCertStoreParameters;
-import java.security.cert.X509CRL;
-import java.security.cert.X509Certificate;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import junit.framework.Test;
-import junit.framework.TestCase;
-import junit.framework.TestSuite;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1OctetString;
-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.ContentInfo;
-import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.cms.CMSConfig;
-import org.bouncycastle.cms.CMSProcessable;
-import org.bouncycastle.cms.CMSProcessableByteArray;
-import org.bouncycastle.cms.CMSSignedData;
-import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.CMSSignedDataParser;
-import org.bouncycastle.cms.SignerId;
-import org.bouncycastle.cms.SignerInformation;
-import org.bouncycastle.cms.SignerInformationStore;
-import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.io.Streams;
-import org.bouncycastle.x509.X509AttributeCertificate;
-import org.bouncycastle.x509.X509CollectionStoreParameters;
-import org.bouncycastle.x509.X509Store;
-
-public class SignedDataTest
-    extends TestCase
-{
-    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
-
-    boolean DEBUG = true;
-
-    private static String          _origDN;
-    private static KeyPair         _origKP;
-    private static X509Certificate _origCert;
-    
-    private static String          _signDN;
-    private static KeyPair         _signKP;
-    private static X509Certificate _signCert;
-    
-    private static KeyPair         _signGostKP;
-    private static X509Certificate _signGostCert;
-
-    private static KeyPair         _signEcDsaKP;
-    private static X509Certificate _signEcDsaCert;
-
-    private static KeyPair         _signEcGostKP;
-    private static X509Certificate _signEcGostCert;
-
-    private static KeyPair         _signDsaKP;
-    private static X509Certificate _signDsaCert;
-    
-    private static String          _reciDN;
-    private static KeyPair         _reciKP;
-    private static X509Certificate _reciCert;
-
-    private static X509CRL         _signCrl;
-
-    private static boolean _initialised = false;
-
-    private byte[] disorderedMessage = Base64.decode(
-            "SU9fc3RkaW5fdXNlZABfX2xpYmNfc3RhcnRfbWFpbgBnZXRob3N0aWQAX19n"
-          + "bW9uX3M=");
-
-    private byte[] disorderedSet = Base64.decode(
-            "MIIYXQYJKoZIhvcNAQcCoIIYTjCCGEoCAQExCzAJBgUrDgMCGgUAMAsGCSqG"
-          + "SIb3DQEHAaCCFqswggJUMIIBwKADAgECAgMMg6wwCgYGKyQDAwECBQAwbzEL"
-          + "MAkGA1UEBhMCREUxPTA7BgNVBAoUNFJlZ3VsaWVydW5nc2JlaMhvcmRlIGbI"
-          + "dXIgVGVsZWtvbW11bmlrYXRpb24gdW5kIFBvc3QxITAMBgcCggYBCgcUEwEx"
-          + "MBEGA1UEAxQKNFItQ0EgMTpQTjAiGA8yMDAwMDMyMjA5NDM1MFoYDzIwMDQw"
-          + "MTIxMTYwNDUzWjBvMQswCQYDVQQGEwJERTE9MDsGA1UEChQ0UmVndWxpZXJ1"
-          + "bmdzYmVoyG9yZGUgZsh1ciBUZWxla29tbXVuaWthdGlvbiB1bmQgUG9zdDEh"
-          + "MAwGBwKCBgEKBxQTATEwEQYDVQQDFAo1Ui1DQSAxOlBOMIGhMA0GCSqGSIb3"
-          + "DQEBAQUAA4GPADCBiwKBgQCKHkFTJx8GmoqFTxEOxpK9XkC3NZ5dBEKiUv0I"
-          + "fe3QMqeGMoCUnyJxwW0k2/53duHxtv2yHSZpFKjrjvE/uGwdOMqBMTjMzkFg"
-          + "19e9JPv061wyADOucOIaNAgha/zFt9XUyrHF21knKCvDNExv2MYIAagkTKaj"
-          + "LMAw0bu1J0FadQIFAMAAAAEwCgYGKyQDAwECBQADgYEAgFauXpoTLh3Z3pT/"
-          + "3bhgrxO/2gKGZopWGSWSJPNwq/U3x2EuctOJurj+y2inTcJjespThflpN+7Q"
-          + "nvsUhXU+jL2MtPlObU0GmLvWbi47cBShJ7KElcZAaxgWMBzdRGqTOdtMv+ev"
-          + "2t4igGF/q71xf6J2c3pTLWr6P8s6tzLfOCMwggJDMIIBr6ADAgECAgQAuzyu"
-          + "MAoGBiskAwMBAgUAMG8xCzAJBgNVBAYTAkRFMT0wOwYDVQQKFDRSZWd1bGll"
-          + "cnVuZ3NiZWjIb3JkZSBmyHVyIFRlbGVrb21tdW5pa2F0aW9uIHVuZCBQb3N0"
-          + "MSEwDAYHAoIGAQoHFBMBMTARBgNVBAMUCjVSLUNBIDE6UE4wIhgPMjAwMTA4"
-          + "MjAwODA4MjBaGA8yMDA1MDgyMDA4MDgyMFowSzELMAkGA1UEBhMCREUxEjAQ"
-          + "BgNVBAoUCVNpZ250cnVzdDEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFDQSBT"
-          + "SUdOVFJVU1QgMTpQTjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAhV12"
-          + "N2WhlR6f+3CXP57GrBM9la5Vnsu2b92zv5MZqQOPeEsYbZqDCFkYg1bSwsDE"
-          + "XsGVQqXdQNAGUaapr/EUVVN+hNZ07GcmC1sPeQECgUkxDYjGi4ihbvzxlahj"
-          + "L4nX+UTzJVBfJwXoIvJ+lMHOSpnOLIuEL3SRhBItvRECxN0CAwEAAaMSMBAw"
-          + "DgYDVR0PAQH/BAQDAgEGMAoGBiskAwMBAgUAA4GBACDc9Pc6X8sK1cerphiV"
-          + "LfFv4kpZb9ev4WPy/C6987Qw1SOTElhZAmxaJQBqmDHWlQ63wj1DEqswk7hG"
-          + "LrvQk/iX6KXIn8e64uit7kx6DHGRKNvNGofPjr1WelGeGW/T2ZJKgmPDjCkf"
-          + "sIKt2c3gwa2pDn4mmCz/DStUIqcPDbqLMIICVTCCAcGgAwIBAgIEAJ16STAK"
-          + "BgYrJAMDAQIFADBvMQswCQYDVQQGEwJERTE9MDsGA1UEChQ0UmVndWxpZXJ1"
-          + "bmdzYmVoyG9yZGUgZsh1ciBUZWxla29tbXVuaWthdGlvbiB1bmQgUG9zdDEh"
-          + "MAwGBwKCBgEKBxQTATEwEQYDVQQDFAo1Ui1DQSAxOlBOMCIYDzIwMDEwMjAx"
-          + "MTM0NDI1WhgPMjAwNTAzMjIwODU1NTFaMG8xCzAJBgNVBAYTAkRFMT0wOwYD"
-          + "VQQKFDRSZWd1bGllcnVuZ3NiZWjIb3JkZSBmyHVyIFRlbGVrb21tdW5pa2F0"
-          + "aW9uIHVuZCBQb3N0MSEwDAYHAoIGAQoHFBMBMTARBgNVBAMUCjZSLUNhIDE6"
-          + "UE4wgaEwDQYJKoZIhvcNAQEBBQADgY8AMIGLAoGBAIOiqxUkzVyqnvthihnl"
-          + "tsE5m1Xn5TZKeR/2MQPStc5hJ+V4yptEtIx+Fn5rOoqT5VEVWhcE35wdbPvg"
-          + "JyQFn5msmhPQT/6XSGOlrWRoFummXN9lQzAjCj1sgTcmoLCVQ5s5WpCAOXFw"
-          + "VWu16qndz3sPItn3jJ0F3Kh3w79NglvPAgUAwAAAATAKBgYrJAMDAQIFAAOB"
-          + "gQBpSRdnDb6AcNVaXSmGo6+kVPIBhot1LzJOGaPyDNpGXxd7LV4tMBF1U7gr"
-          + "4k1g9BO6YiMWvw9uiTZmn0CfV8+k4fWEuG/nmafRoGIuay2f+ILuT+C0rnp1"
-          + "4FgMsEhuVNJJAmb12QV0PZII+UneyhAneZuQQzVUkTcVgYxogxdSOzCCAlUw"
-          + "ggHBoAMCAQICBACdekowCgYGKyQDAwECBQAwbzELMAkGA1UEBhMCREUxPTA7"
-          + "BgNVBAoUNFJlZ3VsaWVydW5nc2JlaMhvcmRlIGbIdXIgVGVsZWtvbW11bmlr"
-          + "YXRpb24gdW5kIFBvc3QxITAMBgcCggYBCgcUEwExMBEGA1UEAxQKNlItQ2Eg"
-          + "MTpQTjAiGA8yMDAxMDIwMTEzNDcwN1oYDzIwMDUwMzIyMDg1NTUxWjBvMQsw"
-          + "CQYDVQQGEwJERTE9MDsGA1UEChQ0UmVndWxpZXJ1bmdzYmVoyG9yZGUgZsh1"
-          + "ciBUZWxla29tbXVuaWthdGlvbiB1bmQgUG9zdDEhMAwGBwKCBgEKBxQTATEw"
-          + "EQYDVQQDFAo1Ui1DQSAxOlBOMIGhMA0GCSqGSIb3DQEBAQUAA4GPADCBiwKB"
-          + "gQCKHkFTJx8GmoqFTxEOxpK9XkC3NZ5dBEKiUv0Ife3QMqeGMoCUnyJxwW0k"
-          + "2/53duHxtv2yHSZpFKjrjvE/uGwdOMqBMTjMzkFg19e9JPv061wyADOucOIa"
-          + "NAgha/zFt9XUyrHF21knKCvDNExv2MYIAagkTKajLMAw0bu1J0FadQIFAMAA"
-          + "AAEwCgYGKyQDAwECBQADgYEAV1yTi+2gyB7sUhn4PXmi/tmBxAfe5oBjDW8m"
-          + "gxtfudxKGZ6l/FUPNcrSc5oqBYxKWtLmf3XX87LcblYsch617jtNTkMzhx9e"
-          + "qxiD02ufcrxz2EVt0Akdqiz8mdVeqp3oLcNU/IttpSrcA91CAnoUXtDZYwb/"
-          + "gdQ4FI9l3+qo/0UwggJVMIIBwaADAgECAgQAxIymMAoGBiskAwMBAgUAMG8x"
-          + "CzAJBgNVBAYTAkRFMT0wOwYDVQQKFDRSZWd1bGllcnVuZ3NiZWjIb3JkZSBm"
-          + "yHVyIFRlbGVrb21tdW5pa2F0aW9uIHVuZCBQb3N0MSEwDAYHAoIGAQoHFBMB"
-          + "MTARBgNVBAMUCjZSLUNhIDE6UE4wIhgPMjAwMTEwMTUxMzMxNThaGA8yMDA1"
-          + "MDYwMTA5NTIxN1owbzELMAkGA1UEBhMCREUxPTA7BgNVBAoUNFJlZ3VsaWVy"
-          + "dW5nc2JlaMhvcmRlIGbIdXIgVGVsZWtvbW11bmlrYXRpb24gdW5kIFBvc3Qx"
-          + "ITAMBgcCggYBCgcUEwExMBEGA1UEAxQKN1ItQ0EgMTpQTjCBoTANBgkqhkiG"
-          + "9w0BAQEFAAOBjwAwgYsCgYEAiokD/j6lEP4FexF356OpU5teUpGGfUKjIrFX"
-          + "BHc79G0TUzgVxqMoN1PWnWktQvKo8ETaugxLkP9/zfX3aAQzDW4Zki6x6GDq"
-          + "fy09Agk+RJvhfbbIzRkV4sBBco0n73x7TfG/9NTgVr/96U+I+z/1j30aboM6"
-          + "9OkLEhjxAr0/GbsCBQDAAAABMAoGBiskAwMBAgUAA4GBAHWRqRixt+EuqHhR"
-          + "K1kIxKGZL2vZuakYV0R24Gv/0ZR52FE4ECr+I49o8FP1qiGSwnXB0SwjuH2S"
-          + "iGiSJi+iH/MeY85IHwW1P5e+bOMvEOFhZhQXQixOD7totIoFtdyaj1XGYRef"
-          + "0f2cPOjNJorXHGV8wuBk+/j++sxbd/Net3FtMIICVTCCAcGgAwIBAgIEAMSM"
-          + "pzAKBgYrJAMDAQIFADBvMQswCQYDVQQGEwJERTE9MDsGA1UEChQ0UmVndWxp"
-          + "ZXJ1bmdzYmVoyG9yZGUgZsh1ciBUZWxla29tbXVuaWthdGlvbiB1bmQgUG9z"
-          + "dDEhMAwGBwKCBgEKBxQTATEwEQYDVQQDFAo3Ui1DQSAxOlBOMCIYDzIwMDEx"
-          + "MDE1MTMzNDE0WhgPMjAwNTA2MDEwOTUyMTdaMG8xCzAJBgNVBAYTAkRFMT0w"
-          + "OwYDVQQKFDRSZWd1bGllcnVuZ3NiZWjIb3JkZSBmyHVyIFRlbGVrb21tdW5p"
-          + "a2F0aW9uIHVuZCBQb3N0MSEwDAYHAoIGAQoHFBMBMTARBgNVBAMUCjZSLUNh"
-          + "IDE6UE4wgaEwDQYJKoZIhvcNAQEBBQADgY8AMIGLAoGBAIOiqxUkzVyqnvth"
-          + "ihnltsE5m1Xn5TZKeR/2MQPStc5hJ+V4yptEtIx+Fn5rOoqT5VEVWhcE35wd"
-          + "bPvgJyQFn5msmhPQT/6XSGOlrWRoFummXN9lQzAjCj1sgTcmoLCVQ5s5WpCA"
-          + "OXFwVWu16qndz3sPItn3jJ0F3Kh3w79NglvPAgUAwAAAATAKBgYrJAMDAQIF"
-          + "AAOBgQBi5W96UVDoNIRkCncqr1LLG9vF9SGBIkvFpLDIIbcvp+CXhlvsdCJl"
-          + "0pt2QEPSDl4cmpOet+CxJTdTuMeBNXxhb7Dvualog69w/+K2JbPhZYxuVFZs"
-          + "Zh5BkPn2FnbNu3YbJhE60aIkikr72J4XZsI5DxpZCGh6xyV/YPRdKSljFjCC"
-          + "AlQwggHAoAMCAQICAwyDqzAKBgYrJAMDAQIFADBvMQswCQYDVQQGEwJERTE9"
-          + "MDsGA1UEChQ0UmVndWxpZXJ1bmdzYmVoyG9yZGUgZsh1ciBUZWxla29tbXVu"
-          + "aWthdGlvbiB1bmQgUG9zdDEhMAwGBwKCBgEKBxQTATEwEQYDVQQDFAo1Ui1D"
-          + "QSAxOlBOMCIYDzIwMDAwMzIyMDk0MTI3WhgPMjAwNDAxMjExNjA0NTNaMG8x"
-          + "CzAJBgNVBAYTAkRFMT0wOwYDVQQKFDRSZWd1bGllcnVuZ3NiZWjIb3JkZSBm"
-          + "yHVyIFRlbGVrb21tdW5pa2F0aW9uIHVuZCBQb3N0MSEwDAYHAoIGAQoHFBMB"
-          + "MTARBgNVBAMUCjRSLUNBIDE6UE4wgaEwDQYJKoZIhvcNAQEBBQADgY8AMIGL"
-          + "AoGBAI8x26tmrFJanlm100B7KGlRemCD1R93PwdnG7svRyf5ZxOsdGrDszNg"
-          + "xg6ouO8ZHQMT3NC2dH8TvO65Js+8bIyTm51azF6clEg0qeWNMKiiXbBXa+ph"
-          + "hTkGbXiLYvACZ6/MTJMJ1lcrjpRF7BXtYeYMcEF6znD4pxOqrtbf9z5hAgUA"
-          + "wAAAATAKBgYrJAMDAQIFAAOBgQB99BjSKlGPbMLQAgXlvA9jUsDNhpnVm3a1"
-          + "YkfxSqS/dbQlYkbOKvCxkPGA9NBxisBM8l1zFynVjJoy++aysRmcnLY/sHaz"
-          + "23BF2iU7WERy18H3lMBfYB6sXkfYiZtvQZcWaO48m73ZBySuiV3iXpb2wgs/"
-          + "Cs20iqroAWxwq/W/9jCCAlMwggG/oAMCAQICBDsFZ9UwCgYGKyQDAwECBQAw"
-          + "bzELMAkGA1UEBhMCREUxITAMBgcCggYBCgcUEwExMBEGA1UEAxQKNFItQ0Eg"
-          + "MTpQTjE9MDsGA1UEChQ0UmVndWxpZXJ1bmdzYmVoyG9yZGUgZsh1ciBUZWxl"
-          + "a29tbXVuaWthdGlvbiB1bmQgUG9zdDAiGA8xOTk5MDEyMTE3MzUzNFoYDzIw"
-          + "MDQwMTIxMTYwMDAyWjBvMQswCQYDVQQGEwJERTE9MDsGA1UEChQ0UmVndWxp"
-          + "ZXJ1bmdzYmVoyG9yZGUgZsh1ciBUZWxla29tbXVuaWthdGlvbiB1bmQgUG9z"
-          + "dDEhMAwGBwKCBgEKBxQTATEwEQYDVQQDFAozUi1DQSAxOlBOMIGfMA0GCSqG"
-          + "SIb3DQEBAQUAA4GNADCBiQKBgI4B557mbKQg/AqWBXNJhaT/6lwV93HUl4U8"
-          + "u35udLq2+u9phns1WZkdM3gDfEpL002PeLfHr1ID/96dDYf04lAXQfombils"
-          + "of1C1k32xOvxjlcrDOuPEMxz9/HDAQZA5MjmmYHAIulGI8Qg4Tc7ERRtg/hd"
-          + "0QX0/zoOeXoDSEOBAgTAAAABMAoGBiskAwMBAgUAA4GBAIyzwfT3keHI/n2P"
-          + "LrarRJv96mCohmDZNpUQdZTVjGu5VQjVJwk3hpagU0o/t/FkdzAjOdfEw8Ql"
-          + "3WXhfIbNLv1YafMm2eWSdeYbLcbB5yJ1od+SYyf9+tm7cwfDAcr22jNRBqx8"
-          + "wkWKtKDjWKkevaSdy99sAI8jebHtWz7jzydKMIID9TCCA16gAwIBAgICbMcw"
-          + "DQYJKoZIhvcNAQEFBQAwSzELMAkGA1UEBhMCREUxEjAQBgNVBAoUCVNpZ250"
-          + "cnVzdDEoMAwGBwKCBgEKBxQTATEwGAYDVQQDFBFDQSBTSUdOVFJVU1QgMTpQ"
-          + "TjAeFw0wNDA3MzAxMzAyNDZaFw0wNzA3MzAxMzAyNDZaMDwxETAPBgNVBAMM"
-          + "CFlhY29tOlBOMQ4wDAYDVQRBDAVZYWNvbTELMAkGA1UEBhMCREUxCjAIBgNV"
-          + "BAUTATEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAIWzLlYLQApocXIp"
-          + "pgCCpkkOUVLgcLYKeOd6/bXAnI2dTHQqT2bv7qzfUnYvOqiNgYdF13pOYtKg"
-          + "XwXMTNFL4ZOI6GoBdNs9TQiZ7KEWnqnr2945HYx7UpgTBclbOK/wGHuCdcwO"
-          + "x7juZs1ZQPFG0Lv8RoiV9s6HP7POqh1sO0P/AgMBAAGjggH1MIIB8TCBnAYD"
-          + "VR0jBIGUMIGRgBQcZzNghfnXoXRm8h1+VITC5caNRqFzpHEwbzELMAkGA1UE"
-          + "BhMCREUxPTA7BgNVBAoUNFJlZ3VsaWVydW5nc2JlaMhvcmRlIGbIdXIgVGVs"
-          + "ZWtvbW11bmlrYXRpb24gdW5kIFBvc3QxITAMBgcCggYBCgcUEwExMBEGA1UE"
-          + "AxQKNVItQ0EgMTpQToIEALs8rjAdBgNVHQ4EFgQU2e5KAzkVuKaM9I5heXkz"
-          + "bcAIuR8wDgYDVR0PAQH/BAQDAgZAMBIGA1UdIAQLMAkwBwYFKyQIAQEwfwYD"
-          + "VR0fBHgwdjB0oCygKoYobGRhcDovL2Rpci5zaWdudHJ1c3QuZGUvbz1TaWdu"
-          + "dHJ1c3QsYz1kZaJEpEIwQDEdMBsGA1UEAxMUQ1JMU2lnblNpZ250cnVzdDE6"
-          + "UE4xEjAQBgNVBAoTCVNpZ250cnVzdDELMAkGA1UEBhMCREUwYgYIKwYBBQUH"
-          + "AQEEVjBUMFIGCCsGAQUFBzABhkZodHRwOi8vZGlyLnNpZ250cnVzdC5kZS9T"
-          + "aWdudHJ1c3QvT0NTUC9zZXJ2bGV0L2h0dHBHYXRld2F5LlBvc3RIYW5kbGVy"
-          + "MBgGCCsGAQUFBwEDBAwwCjAIBgYEAI5GAQEwDgYHAoIGAQoMAAQDAQH/MA0G"
-          + "CSqGSIb3DQEBBQUAA4GBAHn1m3GcoyD5GBkKUY/OdtD6Sj38LYqYCF+qDbJR"
-          + "6pqUBjY2wsvXepUppEler+stH8mwpDDSJXrJyuzf7xroDs4dkLl+Rs2x+2tg"
-          + "BjU+ABkBDMsym2WpwgA8LCdymmXmjdv9tULxY+ec2pjSEzql6nEZNEfrU8nt"
-          + "ZCSCavgqW4TtMYIBejCCAXYCAQEwUTBLMQswCQYDVQQGEwJERTESMBAGA1UE"
-          + "ChQJU2lnbnRydXN0MSgwDAYHAoIGAQoHFBMBMTAYBgNVBAMUEUNBIFNJR05U"
-          + "UlVTVCAxOlBOAgJsxzAJBgUrDgMCGgUAoIGAMBgGCSqGSIb3DQEJAzELBgkq"
-          + "hkiG9w0BBwEwIwYJKoZIhvcNAQkEMRYEFIYfhPoyfGzkLWWSSLjaHb4HQmaK"
-          + "MBwGCSqGSIb3DQEJBTEPFw0wNTAzMjQwNzM4MzVaMCEGBSskCAYFMRgWFi92"
-          + "YXIvZmlsZXMvdG1wXzEvdGVzdDEwDQYJKoZIhvcNAQEFBQAEgYA2IvA8lhVz"
-          + "VD5e/itUxbFboKxeKnqJ5n/KuO/uBCl1N14+7Z2vtw1sfkIG+bJdp3OY2Cmn"
-          + "mrQcwsN99Vjal4cXVj8t+DJzFG9tK9dSLvD3q9zT/GQ0kJXfimLVwCa4NaSf"
-          + "Qsu4xtG0Rav6bCcnzabAkKuNNvKtH8amSRzk870DBg==");
-
-    public static byte[] xtraCounterSig = Base64.decode(
-                 "MIIR/AYJKoZIhvcNAQcCoIIR7TCCEekCAQExCzAJBgUrDgMCGgUAMBoGCSqG"
-               + "SIb3DQEHAaANBAtIZWxsbyB3b3JsZKCCDnkwggTPMIIDt6ADAgECAgRDnYD3"
-               + "MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNVBAYTAklUMRowGAYDVQQKExFJbi5U"
-               + "ZS5TLkEuIFMucC5BLjEtMCsGA1UEAxMkSW4uVGUuUy5BLiAtIENlcnRpZmlj"
-               + "YXRpb24gQXV0aG9yaXR5MB4XDTA4MDkxMjExNDMxMloXDTEwMDkxMjExNDMx"
-               + "MlowgdgxCzAJBgNVBAYTAklUMSIwIAYDVQQKDBlJbnRlc2EgUy5wLkEuLzA1"
-               + "MjYyODkwMDE0MSowKAYDVQQLDCFCdXNpbmVzcyBDb2xsYWJvcmF0aW9uICYg"
-               + "U2VjdXJpdHkxHjAcBgNVBAMMFU1BU1NJTUlMSUFOTyBaSUNDQVJESTERMA8G"
-               + "A1UEBAwIWklDQ0FSREkxFTATBgNVBCoMDE1BU1NJTUlMSUFOTzEcMBoGA1UE"
-               + "BRMTSVQ6WkNDTVNNNzZIMTRMMjE5WTERMA8GA1UELhMIMDAwMDI1ODUwgaAw"
-               + "DQYJKoZIhvcNAQEBBQADgY4AMIGKAoGBALeJTjmyFgx1SIP6c2AuB/kuyHo5"
-               + "j/prKELTALsFDimre/Hxr3wOSet1TdQfFzU8Lu+EJqgfV9cV+cI1yeH1rZs7"
-               + "lei7L3tX/VR565IywnguX5xwvteASgWZr537Fkws50bvTEMyYOj1Tf3FZvZU"
-               + "z4n4OD39KI4mfR9i1eEVIxR3AgQAizpNo4IBoTCCAZ0wHQYDVR0RBBYwFIES"
-               + "emljY2FyZGlAaW50ZXNhLml0MC8GCCsGAQUFBwEDBCMwITAIBgYEAI5GAQEw"
-               + "CwYGBACORgEDAgEUMAgGBgQAjkYBBDBZBgNVHSAEUjBQME4GBgQAizABATBE"
-               + "MEIGCCsGAQUFBwIBFjZodHRwOi8vZS10cnVzdGNvbS5pbnRlc2EuaXQvY2Ff"
-               + "cHViYmxpY2EvQ1BTX0lOVEVTQS5odG0wDgYDVR0PAQH/BAQDAgZAMIGDBgNV"
-               + "HSMEfDB6gBQZCQOW0bjFWBt+EORuxPagEgkQqKFcpFowWDELMAkGA1UEBhMC"
-               + "SVQxGjAYBgNVBAoTEUluLlRlLlMuQS4gUy5wLkEuMS0wKwYDVQQDEyRJbi5U"
-               + "ZS5TLkEuIC0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHmCBDzRARMwOwYDVR0f"
-               + "BDQwMjAwoC6gLIYqaHR0cDovL2UtdHJ1c3Rjb20uaW50ZXNhLml0L0NSTC9J"
-               + "TlRFU0EuY3JsMB0GA1UdDgQWBBTf5ItL8KmQh541Dxt7YxcWI1254TANBgkq"
-               + "hkiG9w0BAQUFAAOCAQEAgW+uL1CVWQepbC/wfCmR6PN37Sueb4xiKQj2mTD5"
-               + "UZ5KQjpivy/Hbuf0NrfKNiDEhAvoHSPC31ebGiKuTMFNyZPHfPEUnyYGSxea"
-               + "2w837aXJFr6utPNQGBRi89kH90sZDlXtOSrZI+AzJJn5QK3F9gjcayU2NZXQ"
-               + "MJgRwYmFyn2w4jtox+CwXPQ9E5XgxiMZ4WDL03cWVXDLX00EOJwnDDMUNTRI"
-               + "m9Zv+4SKTNlfFbi9UTBqWBySkDzAelsfB2U61oqc2h1xKmCtkGMmN9iZT+Qz"
-               + "ZC/vaaT+hLEBFGAH2gwFrYc4/jTBKyBYeU1vsAxsibIoTs1Apgl6MH75qPDL"
-               + "BzCCBM8wggO3oAMCAQICBEOdgPcwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE"
-               + "BhMCSVQxGjAYBgNVBAoTEUluLlRlLlMuQS4gUy5wLkEuMS0wKwYDVQQDEyRJ"
-               + "bi5UZS5TLkEuIC0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkwHhcNMDgwOTEy"
-               + "MTE0MzEyWhcNMTAwOTEyMTE0MzEyWjCB2DELMAkGA1UEBhMCSVQxIjAgBgNV"
-               + "BAoMGUludGVzYSBTLnAuQS4vMDUyNjI4OTAwMTQxKjAoBgNVBAsMIUJ1c2lu"
-               + "ZXNzIENvbGxhYm9yYXRpb24gJiBTZWN1cml0eTEeMBwGA1UEAwwVTUFTU0lN"
-               + "SUxJQU5PIFpJQ0NBUkRJMREwDwYDVQQEDAhaSUNDQVJESTEVMBMGA1UEKgwM"
-               + "TUFTU0lNSUxJQU5PMRwwGgYDVQQFExNJVDpaQ0NNU003NkgxNEwyMTlZMREw"
-               + "DwYDVQQuEwgwMDAwMjU4NTCBoDANBgkqhkiG9w0BAQEFAAOBjgAwgYoCgYEA"
-               + "t4lOObIWDHVIg/pzYC4H+S7IejmP+msoQtMAuwUOKat78fGvfA5J63VN1B8X"
-               + "NTwu74QmqB9X1xX5wjXJ4fWtmzuV6Lsve1f9VHnrkjLCeC5fnHC+14BKBZmv"
-               + "nfsWTCznRu9MQzJg6PVN/cVm9lTPifg4Pf0ojiZ9H2LV4RUjFHcCBACLOk2j"
-               + "ggGhMIIBnTAdBgNVHREEFjAUgRJ6aWNjYXJkaUBpbnRlc2EuaXQwLwYIKwYB"
-               + "BQUHAQMEIzAhMAgGBgQAjkYBATALBgYEAI5GAQMCARQwCAYGBACORgEEMFkG"
-               + "A1UdIARSMFAwTgYGBACLMAEBMEQwQgYIKwYBBQUHAgEWNmh0dHA6Ly9lLXRy"
-               + "dXN0Y29tLmludGVzYS5pdC9jYV9wdWJibGljYS9DUFNfSU5URVNBLmh0bTAO"
-               + "BgNVHQ8BAf8EBAMCBkAwgYMGA1UdIwR8MHqAFBkJA5bRuMVYG34Q5G7E9qAS"
-               + "CRCooVykWjBYMQswCQYDVQQGEwJJVDEaMBgGA1UEChMRSW4uVGUuUy5BLiBT"
-               + "LnAuQS4xLTArBgNVBAMTJEluLlRlLlMuQS4gLSBDZXJ0aWZpY2F0aW9uIEF1"
-               + "dGhvcml0eYIEPNEBEzA7BgNVHR8ENDAyMDCgLqAshipodHRwOi8vZS10cnVz"
-               + "dGNvbS5pbnRlc2EuaXQvQ1JML0lOVEVTQS5jcmwwHQYDVR0OBBYEFN/ki0vw"
-               + "qZCHnjUPG3tjFxYjXbnhMA0GCSqGSIb3DQEBBQUAA4IBAQCBb64vUJVZB6ls"
-               + "L/B8KZHo83ftK55vjGIpCPaZMPlRnkpCOmK/L8du5/Q2t8o2IMSEC+gdI8Lf"
-               + "V5saIq5MwU3Jk8d88RSfJgZLF5rbDzftpckWvq6081AYFGLz2Qf3SxkOVe05"
-               + "Ktkj4DMkmflArcX2CNxrJTY1ldAwmBHBiYXKfbDiO2jH4LBc9D0TleDGIxnh"
-               + "YMvTdxZVcMtfTQQ4nCcMMxQ1NEib1m/7hIpM2V8VuL1RMGpYHJKQPMB6Wx8H"
-               + "ZTrWipzaHXEqYK2QYyY32JlP5DNkL+9ppP6EsQEUYAfaDAWthzj+NMErIFh5"
-               + "TW+wDGyJsihOzUCmCXowfvmo8MsHMIIEzzCCA7egAwIBAgIEQ52A9zANBgkq"
-               + "hkiG9w0BAQUFADBYMQswCQYDVQQGEwJJVDEaMBgGA1UEChMRSW4uVGUuUy5B"
-               + "LiBTLnAuQS4xLTArBgNVBAMTJEluLlRlLlMuQS4gLSBDZXJ0aWZpY2F0aW9u"
-               + "IEF1dGhvcml0eTAeFw0wODA5MTIxMTQzMTJaFw0xMDA5MTIxMTQzMTJaMIHY"
-               + "MQswCQYDVQQGEwJJVDEiMCAGA1UECgwZSW50ZXNhIFMucC5BLi8wNTI2Mjg5"
-               + "MDAxNDEqMCgGA1UECwwhQnVzaW5lc3MgQ29sbGFib3JhdGlvbiAmIFNlY3Vy"
-               + "aXR5MR4wHAYDVQQDDBVNQVNTSU1JTElBTk8gWklDQ0FSREkxETAPBgNVBAQM"
-               + "CFpJQ0NBUkRJMRUwEwYDVQQqDAxNQVNTSU1JTElBTk8xHDAaBgNVBAUTE0lU"
-               + "OlpDQ01TTTc2SDE0TDIxOVkxETAPBgNVBC4TCDAwMDAyNTg1MIGgMA0GCSqG"
-               + "SIb3DQEBAQUAA4GOADCBigKBgQC3iU45shYMdUiD+nNgLgf5Lsh6OY/6ayhC"
-               + "0wC7BQ4pq3vx8a98DknrdU3UHxc1PC7vhCaoH1fXFfnCNcnh9a2bO5Xouy97"
-               + "V/1UeeuSMsJ4Ll+ccL7XgEoFma+d+xZMLOdG70xDMmDo9U39xWb2VM+J+Dg9"
-               + "/SiOJn0fYtXhFSMUdwIEAIs6TaOCAaEwggGdMB0GA1UdEQQWMBSBEnppY2Nh"
-               + "cmRpQGludGVzYS5pdDAvBggrBgEFBQcBAwQjMCEwCAYGBACORgEBMAsGBgQA"
-               + "jkYBAwIBFDAIBgYEAI5GAQQwWQYDVR0gBFIwUDBOBgYEAIswAQEwRDBCBggr"
-               + "BgEFBQcCARY2aHR0cDovL2UtdHJ1c3Rjb20uaW50ZXNhLml0L2NhX3B1YmJs"
-               + "aWNhL0NQU19JTlRFU0EuaHRtMA4GA1UdDwEB/wQEAwIGQDCBgwYDVR0jBHww"
-               + "eoAUGQkDltG4xVgbfhDkbsT2oBIJEKihXKRaMFgxCzAJBgNVBAYTAklUMRow"
-               + "GAYDVQQKExFJbi5UZS5TLkEuIFMucC5BLjEtMCsGA1UEAxMkSW4uVGUuUy5B"
-               + "LiAtIENlcnRpZmljYXRpb24gQXV0aG9yaXR5ggQ80QETMDsGA1UdHwQ0MDIw"
-               + "MKAuoCyGKmh0dHA6Ly9lLXRydXN0Y29tLmludGVzYS5pdC9DUkwvSU5URVNB"
-               + "LmNybDAdBgNVHQ4EFgQU3+SLS/CpkIeeNQ8be2MXFiNdueEwDQYJKoZIhvcN"
-               + "AQEFBQADggEBAIFvri9QlVkHqWwv8Hwpkejzd+0rnm+MYikI9pkw+VGeSkI6"
-               + "Yr8vx27n9Da3yjYgxIQL6B0jwt9XmxoirkzBTcmTx3zxFJ8mBksXmtsPN+2l"
-               + "yRa+rrTzUBgUYvPZB/dLGQ5V7Tkq2SPgMySZ+UCtxfYI3GslNjWV0DCYEcGJ"
-               + "hcp9sOI7aMfgsFz0PROV4MYjGeFgy9N3FlVwy19NBDicJwwzFDU0SJvWb/uE"
-               + "ikzZXxW4vVEwalgckpA8wHpbHwdlOtaKnNodcSpgrZBjJjfYmU/kM2Qv72mk"
-               + "/oSxARRgB9oMBa2HOP40wSsgWHlNb7AMbImyKE7NQKYJejB++ajwywcxggM8"
-               + "MIIDOAIBATBgMFgxCzAJBgNVBAYTAklUMRowGAYDVQQKExFJbi5UZS5TLkEu"
-               + "IFMucC5BLjEtMCsGA1UEAxMkSW4uVGUuUy5BLiAtIENlcnRpZmljYXRpb24g"
-               + "QXV0aG9yaXR5AgRDnYD3MAkGBSsOAwIaBQAwDQYJKoZIhvcNAQEBBQAEgYB+"
-               + "lH2cwLqc91mP8prvgSV+RRzk13dJdZvdoVjgQoFrPhBiZCNIEoHvIhMMA/sM"
-               + "X6euSRZk7EjD24FasCEGYyd0mJVLEy6TSPmuW+wWz/28w3a6IWXBGrbb/ild"
-               + "/CJMkPgLPGgOVD1WDwiNKwfasiQSFtySf5DPn3jFevdLeMmEY6GCAjIwggEV"
-               + "BgkqhkiG9w0BCQYxggEGMIIBAgIBATBgMFgxCzAJBgNVBAYTAklUMRowGAYD"
-               + "VQQKExFJbi5UZS5TLkEuIFMucC5BLjEtMCsGA1UEAxMkSW4uVGUuUy5BLiAt"
-               + "IENlcnRpZmljYXRpb24gQXV0aG9yaXR5AgRDnYD3MAkGBSsOAwIaBQAwDQYJ"
-               + "KoZIhvcNAQEBBQAEgYBHlOULfT5GDigIvxP0qZOy8VbpntmzaPF55VV4buKV"
-               + "35J+uHp98gXKp0LrHM69V5IRKuyuQzHHFBqsXxsRI9o6KoOfgliD9Xc+BeMg"
-               + "dKzQhBhBYoFREq8hQM0nSbqDNHYAQyNHMzUA/ZQUO5dlFuH8Dw3iDYAhNtfd"
-               + "PrlchKJthDCCARUGCSqGSIb3DQEJBjGCAQYwggECAgEBMGAwWDELMAkGA1UE"
-               + "BhMCSVQxGjAYBgNVBAoTEUluLlRlLlMuQS4gUy5wLkEuMS0wKwYDVQQDEyRJ"
-               + "bi5UZS5TLkEuIC0gQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkCBEOdgPcwCQYF"
-               + "Kw4DAhoFADANBgkqhkiG9w0BAQEFAASBgEeU5Qt9PkYOKAi/E/Spk7LxVume"
-               + "2bNo8XnlVXhu4pXfkn64en3yBcqnQusczr1XkhEq7K5DMccUGqxfGxEj2joq"
-               + "g5+CWIP1dz4F4yB0rNCEGEFigVESryFAzSdJuoM0dgBDI0czNQD9lBQ7l2UW"
-               + "4fwPDeINgCE2190+uVyEom2E");
-
-    byte[] noSignedAttrSample2 = Base64.decode(
-          "MIIIlAYJKoZIhvcNAQcCoIIIhTCCCIECAQExCzAJBgUrDgMCGgUAMAsGCSqG"
-        + "SIb3DQEHAaCCB3UwggOtMIIDa6ADAgECAgEzMAsGByqGSM44BAMFADCBkDEL"
-        + "MAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlQYWxvIEFsdG8x"
-        + "HTAbBgNVBAoTFFN1biBNaWNyb3N5c3RlbXMgSW5jMSMwIQYDVQQLExpKYXZh"
-        + "IFNvZnR3YXJlIENvZGUgU2lnbmluZzEcMBoGA1UEAxMTSkNFIENvZGUgU2ln"
-        + "bmluZyBDQTAeFw0wMTA1MjkxNjQ3MTFaFw0wNjA1MjgxNjQ3MTFaMG4xHTAb"
-        + "BgNVBAoTFFN1biBNaWNyb3N5c3RlbXMgSW5jMSMwIQYDVQQLExpKYXZhIFNv"
-        + "ZnR3YXJlIENvZGUgU2lnbmluZzEoMCYGA1UEAxMfVGhlIExlZ2lvbiBvZiB0"
-        + "aGUgQm91bmN5IENhc3RsZTCCAbcwggEsBgcqhkjOOAQBMIIBHwKBgQD9f1OB"
-        + "HXUSKVLfSpwu7OTn9hG3UjzvRADDHj+AtlEmaUVdQCJR+1k9jVj6v8X1ujD2"
-        + "y5tVbNeBO4AdNG/yZmC3a5lQpaSfn+gEexAiwk+7qdf+t8Yb+DtX58aophUP"
-        + "BPuD9tPFHsMCNVQTWhaRMvZ1864rYdcq7/IiAxmd0UgBxwIVAJdgUI8VIwvM"
-        + "spK5gqLrhAvwWBz1AoGBAPfhoIXWmz3ey7yrXDa4V7l5lK+7+jrqgvlXTAs9"
-        + "B4JnUVlXjrrUWU/mcQcQgYC0SRZxI+hMKBYTt88JMozIpuE8FnqLVHyNKOCj"
-        + "rh4rs6Z1kW6jfwv6ITVi8ftiegEkO8yk8b6oUZCJqIPf4VrlnwaSi2ZegHtV"
-        + "JWQBTDv+z0kqA4GEAAKBgBWry/FCAZ6miyy39+ftsa+h9lxoL+JtV0MJcUyQ"
-        + "E4VAhpAwWb8vyjba9AwOylYQTktHX5sAkFvjBiU0LOYDbFSTVZSHMRJgfjxB"
-        + "SHtICjOEvr1BJrrOrdzqdxcOUge5n7El124BCrv91x5Ol8UTwtiO9LrRXF/d"
-        + "SyK+RT5n1klRo3YwdDARBglghkgBhvhCAQEEBAMCAIcwDgYDVR0PAQH/BAQD"
-        + "AgHGMB0GA1UdDgQWBBQwMY4NRcco1AO3w1YsokfDLVseEjAPBgNVHRMBAf8E"
-        + "BTADAQH/MB8GA1UdIwQYMBaAFGXi9IbJ007wkU5Yomr12HhamsGmMAsGByqG"
-        + "SM44BAMFAAMvADAsAhRmigTu6QV0sTfEkVljgij/hhdVfAIUQZvMxAnIHc30"
-        + "y/u0C1T5UEG9glUwggPAMIIDfqADAgECAgEQMAsGByqGSM44BAMFADCBkDEL"
-        + "MAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRIwEAYDVQQHEwlQYWxvIEFsdG8x"
-        + "HTAbBgNVBAoTFFN1biBNaWNyb3N5c3RlbXMgSW5jMSMwIQYDVQQLExpKYXZh"
-        + "IFNvZnR3YXJlIENvZGUgU2lnbmluZzEcMBoGA1UEAxMTSkNFIENvZGUgU2ln"
-        + "bmluZyBDQTAeFw0wMTA0MjUwNzAwMDBaFw0yMDA0MjUwNzAwMDBaMIGQMQsw"
-        + "CQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0bzEd"
-        + "MBsGA1UEChMUU3VuIE1pY3Jvc3lzdGVtcyBJbmMxIzAhBgNVBAsTGkphdmEg"
-        + "U29mdHdhcmUgQ29kZSBTaWduaW5nMRwwGgYDVQQDExNKQ0UgQ29kZSBTaWdu"
-        + "aW5nIENBMIIBtzCCASwGByqGSM44BAEwggEfAoGBAOuvNwQeylEeaV2w8o/2"
-        + "tUkfxqSZBdcpv3S3avUZ2B7kG/gKAZqY/3Cr4kpWhmxTs/zhyIGMMfDE87CL"
-        + "5nAG7PdpaNuDTHIpiSk2F1w7SgegIAIqRpdRHXDICBgLzgxum3b3BePn+9Nh"
-        + "eeFgmiSNBpWDPFEg4TDPOFeCphpyDc7TAhUAhCVF4bq5qWKreehbMLiJaxv/"
-        + "e3UCgYEAq8l0e3Tv7kK1alNNO92QBnJokQ8LpCl2LlU71a5NZVx+KjoEpmem"
-        + "0HGqpde34sFyDaTRqh6SVEwgAAmisAlBGTMAssNcrkL4sYvKfJbYEH83RFuq"
-        + "zHjI13J2N2tAmahVZvqoAx6LShECactMuCUGHKB30sms0j3pChD6dnC3+9wD"
-        + "gYQAAoGALQmYXKy4nMeZfu4gGSo0kPnXq6uu3WtylQ1m+O8nj0Sy7ShEx/6v"
-        + "sKYnbwBnRYJbB6hWVjvSKVFhXmk51y50dxLPGUr1LcjLcmHETm/6R0M/FLv6"
-        + "vBhmKMLZZot6LS/CYJJLFP5YPiF/aGK+bEhJ+aBLXoWdGRD5FUVRG3HU9wuj"
-        + "ZjBkMBEGCWCGSAGG+EIBAQQEAwIABzAPBgNVHRMBAf8EBTADAQH/MB8GA1Ud"
-        + "IwQYMBaAFGXi9IbJ007wkU5Yomr12HhamsGmMB0GA1UdDgQWBBRl4vSGydNO"
-        + "8JFOWKJq9dh4WprBpjALBgcqhkjOOAQDBQADLwAwLAIUKvfPPJdd+Xi2CNdB"
-        + "tNkNRUzktJwCFEXNdWkOIfod1rMpsun3Mx0z/fxJMYHoMIHlAgEBMIGWMIGQ"
-        + "MQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExEjAQBgNVBAcTCVBhbG8gQWx0"
-        + "bzEdMBsGA1UEChMUU3VuIE1pY3Jvc3lzdGVtcyBJbmMxIzAhBgNVBAsTGkph"
-        + "dmEgU29mdHdhcmUgQ29kZSBTaWduaW5nMRwwGgYDVQQDExNKQ0UgQ29kZSBT"
-        + "aWduaW5nIENBAgEzMAkGBSsOAwIaBQAwCwYHKoZIzjgEAQUABC8wLQIVAIGV"
-        + "khm+kbV4a/+EP45PHcq0hIViAhR4M9os6IrJnoEDS3Y3l7O6zrSosA==");
-
-    private JcaX509CertSelectorConverter selectorConverter = new JcaX509CertSelectorConverter();
-
-    /*
-     *
-     *  INFRASTRUCTURE
-     *
-     */
-
-    public SignedDataTest(String name)
-    {
-        super(name);
-    }
-
-    public static void main(String args[])
-    {
-
-        junit.textui.TestRunner.run(SignedDataTest.class);
-    }
-
-    public static Test suite() 
-        throws Exception
-    {
-        init();
-        
-        return new CMSTestSetup(new TestSuite(SignedDataTest.class));
-    }
-
-    private static void init()
-        throws Exception
-    {
-        if (!_initialised)
-        {
-            _initialised = true;
-            
-            _origDN   = "O=Bouncy Castle, C=AU";
-            _origKP   = CMSTestUtil.makeKeyPair();  
-            _origCert = CMSTestUtil.makeCertificate(_origKP, _origDN, _origKP, _origDN);
-
-            _signDN   = "CN=Bob, OU=Sales, O=Bouncy Castle, C=AU";
-            _signKP   = CMSTestUtil.makeKeyPair();
-            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _origKP, _origDN);
-            
-            _signGostKP   = CMSTestUtil.makeGostKeyPair();
-            _signGostCert = CMSTestUtil.makeCertificate(_signGostKP, _signDN, _origKP, _origDN);
-    
-            _signDsaKP   = CMSTestUtil.makeDsaKeyPair();
-            _signDsaCert = CMSTestUtil.makeCertificate(_signDsaKP, _signDN, _origKP, _origDN);
-            
-            _signEcDsaKP   = CMSTestUtil.makeEcDsaKeyPair();
-            _signEcDsaCert = CMSTestUtil.makeCertificate(_signEcDsaKP, _signDN, _origKP, _origDN);
-
-            _signEcGostKP = CMSTestUtil.makeEcGostKeyPair();
-            _signEcGostCert = CMSTestUtil.makeCertificate(_signEcGostKP, _signDN, _origKP, _origDN);
-
-            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
-            _reciKP   = CMSTestUtil.makeKeyPair();
-            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
-
-            _signCrl  = CMSTestUtil.makeCrl(_signKP);
-        }
-    }
-
-    private void verifySignatures(CMSSignedData s, byte[] contentDigest) 
-        throws Exception
-    {
-        CertStore               certStore = s.getCertificatesAndCRLs("Collection", BC);
-        SignerInformationStore  signers = s.getSignerInfos();
-        
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certStore.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-    
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-    
-            assertEquals(true, signer.verify(cert, BC));
-            
-            if (contentDigest != null)
-            {
-                assertTrue(MessageDigest.isEqual(contentDigest, signer.getContentDigest()));
-            }
-        }
-
-        Collection certColl = certStore.getCertificates(null);
-        Collection crlColl = certStore.getCRLs(null);
-
-        assertEquals(certColl.size(), s.getCertificates("Collection", BC).getMatches(null).size());
-        assertEquals(crlColl.size(), s.getCRLs("Collection", BC).getMatches(null).size());
-    }
-
-    private void verifySignatures(CMSSignedData s) 
-        throws Exception
-    {
-        verifySignatures(s, null);
-    }
-
-    public void testDetachedVerification()
-        throws Exception
-    {
-        byte[]              data = "Hello World!".getBytes();
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray(data);
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_MD5);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData s = gen.generate(msg, BC);
-
-        MessageDigest sha1 = MessageDigest.getInstance("SHA1", BC);
-        MessageDigest md5 = MessageDigest.getInstance("MD5", BC);
-        Map hashes = new HashMap();
-        byte[] sha1Hash = sha1.digest(data);
-        byte[] md5Hash = md5.digest(data);
-
-        hashes.put(CMSSignedDataGenerator.DIGEST_SHA1, sha1Hash);
-        hashes.put(CMSSignedDataGenerator.DIGEST_MD5, md5Hash);
-
-        s = new CMSSignedData(hashes, s.getEncoded());
-
-        verifySignatures(s, null);
-    }
-
-    public void testSHA1AndMD5WithRSAEncapsulatedRepeated()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_MD5);
-        
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData s = gen.generate(msg, true, BC);
-
-        ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
-        ASN1InputStream      aIn = new ASN1InputStream(bIn);
-        
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-
-        certs = s.getCertificatesAndCRLs("Collection", BC);
-
-        SignerInformationStore  signers = s.getSignerInfos();
-        
-        assertEquals(2, signers.size());
-        
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-        SignerId                sid = null;
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            sid = signer.getSID();
-            
-            assertEquals(true, signer.verify(cert, BC));
-
-            //
-            // check content digest
-            //
-
-            byte[] contentDigest = (byte[])gen.getGeneratedDigests().get(signer.getDigestAlgOID());
-
-            AttributeTable table = signer.getSignedAttributes();
-            Attribute hash = table.get(CMSAttributes.messageDigest);
-
-            assertTrue(MessageDigest.isEqual(contentDigest, ((ASN1OctetString)hash.getAttrValues().getObjectAt(0)).getOctets()));
-        }
-        
-        c = signers.getSigners(sid);
-        
-        assertEquals(2, c.size());
-
-
-        //
-        // try using existing signer
-        //
-        
-        gen = new CMSSignedDataGenerator();
-           
-        gen.addSigners(s.getSignerInfos());
-        
-        gen.addCertificatesAndCRLs(s.getCertificatesAndCRLs("Collection", BC));
-           
-        s = gen.generate(msg, true, BC);
-
-        bIn = new ByteArrayInputStream(s.getEncoded());
-        aIn = new ASN1InputStream(bIn);
-
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-
-        certs = s.getCertificatesAndCRLs("Collection", BC);
-
-        signers = s.getSignerInfos();
-        c = signers.getSigners();
-        it = c.iterator();
-
-        assertEquals(2, c.size());
-        
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertEquals(true, signer.verify(cert, BC));
-        }
-        
-        checkSignerStoreReplacement(s, signers);
-    }
-    
-    public void testSHA1WithRSANoAttributes()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello world!".getBytes());
-    
-        certList.add(_origCert);
-        certList.add(_signCert);
-    
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-    
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-    
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    
-        gen.addCertificatesAndCRLs(certs);
-    
-        CMSSignedData s = gen.generate(CMSSignedDataGenerator.DATA, msg, false, BC, false);
-    
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance("SHA1", BC);
-        
-        verifySignatures(s, md.digest("Hello world!".getBytes()));
-    }
-
-    public void testSHA1WithRSAViaConfig()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello world!".getBytes());
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        // set some bogus mappings.
-        CMSConfig.setSigningEncryptionAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption.getId(), "XXXX");
-        CMSConfig.setSigningDigestAlgorithmMapping(OIWObjectIdentifiers.idSHA1.getId(), "YYYY");
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData s;
-
-        try
-        {
-            // try the bogus mappings
-            s = gen.generate(CMSSignedDataGenerator.DATA, msg, false, BC, false);
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            if (!e.getMessage().startsWith("Unknown signature type requested: YYYYWITHXXXX"))
-            {
-                throw e;
-            }
-        }
-        finally
-        {
-            // reset to the real ones
-            CMSConfig.setSigningEncryptionAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption.getId(), "RSA");
-            CMSConfig.setSigningDigestAlgorithmMapping(OIWObjectIdentifiers.idSHA1.getId(), "SHA1"); 
-        }
-
-        s = gen.generate(CMSSignedDataGenerator.DATA, msg, false, BC, false);
-
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance("SHA1", BC);
-
-        verifySignatures(s, md.digest("Hello world!".getBytes()));
-    }
-
-    public void testSHA1WithRSAAndAttributeTable()
-        throws Exception
-    {
-        MessageDigest       md = MessageDigest.getInstance("SHA1", BC);
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello world!".getBytes());
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        Attribute attr = new Attribute(CMSAttributes.messageDigest,
-                                       new DERSet(
-                                            new DEROctetString(
-                                                md.digest("Hello world!".getBytes()))));
-
-        ASN1EncodableVector v = new ASN1EncodableVector();
-
-        v.add(attr);
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1, new AttributeTable(v), null);
-
-        gen.addCertificatesAndCRLs(certs);
-
-
-        CMSSignedData s = gen.generate(CMSSignedDataGenerator.DATA, null, false, BC);
-
-        //
-        // the signature is detached, so need to add msg before passing on
-        //
-        s = new CMSSignedData(msg, s.getEncoded());
-        //
-        // compute expected content digest
-        //
-
-        verifySignatures(s, md.digest("Hello world!".getBytes()));
-    }
-
-    public void testSHA1WithRSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-
-    public void testSHA1WithRSAEncapsulatedSubjectKeyID()
-        throws Exception
-    {
-        subjectKeyIDTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-
-    public void testSHA1WithRSAPSS()
-        throws Exception
-    {
-        rsaPSSTest("SHA1", CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-
-    public void testSHA224WithRSAPSS()
-        throws Exception
-    {
-        rsaPSSTest("SHA224", CMSSignedDataGenerator.DIGEST_SHA224);
-    }
-
-    public void testSHA256WithRSAPSS()
-        throws Exception
-    {
-        rsaPSSTest("SHA256", CMSSignedDataGenerator.DIGEST_SHA256);
-    }
-
-    public void testSHA384WithRSAPSS()
-        throws Exception
-    {
-        rsaPSSTest("SHA384", CMSSignedDataGenerator.DIGEST_SHA384);
-    }
-
-    public void testSHA224WithRSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_SHA224);
-    }
-    
-    public void testSHA256WithRSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_SHA256);
-    }
-
-    public void testRIPEMD128WithRSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_RIPEMD128);
-    }
-
-    public void testRIPEMD160WithRSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_RIPEMD160);
-    }
-
-    public void testRIPEMD256WithRSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signKP, _signCert, CMSSignedDataGenerator.DIGEST_RIPEMD256);
-    }
-
-    public void testECDSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-
-    public void testECDSAEncapsulatedSubjectKeyID()
-        throws Exception
-    {
-        subjectKeyIDTest(_signEcDsaKP, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-
-    public void testECDSASHA224Encapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA224);
-    }
-
-    public void testECDSASHA256Encapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA256);
-    }
-
-    public void testECDSASHA384Encapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA384);
-    }
-
-    public void testECDSASHA512Encapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA512);
-    }
-
-    public void testECDSASHA512EncapsulatedWithKeyFactoryAsEC()
-        throws Exception
-    {
-        X509EncodedKeySpec  pubSpec = new X509EncodedKeySpec(_signEcDsaKP.getPublic().getEncoded());
-        PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(_signEcDsaKP.getPrivate().getEncoded());
-        KeyFactory          keyFact = KeyFactory.getInstance("EC", BC);
-        KeyPair             kp = new KeyPair(keyFact.generatePublic(pubSpec), keyFact.generatePrivate(privSpec));
-        
-        encapsulatedTest(kp, _signEcDsaCert, CMSSignedDataGenerator.DIGEST_SHA512);
-    }
-
-    public void testDSAEncapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signDsaKP, _signDsaCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-
-    public void testDSAEncapsulatedSubjectKeyID()
-        throws Exception
-    {
-        subjectKeyIDTest(_signDsaKP, _signDsaCert, CMSSignedDataGenerator.DIGEST_SHA1);
-    }
-        
-    public void testGOST3411WithGOST3410Encapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signGostKP, _signGostCert, CMSSignedDataGenerator.DIGEST_GOST3411);
-    }
-
-    public void testGOST3411WithECGOST3410Encapsulated()
-        throws Exception
-    {
-        encapsulatedTest(_signEcGostKP, _signEcGostCert, CMSSignedDataGenerator.DIGEST_GOST3411);
-    }
-
-    public void testSHA1WithRSACounterSignature()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-        certList.add(_signCert);
-        certList.add(_origCert);
-
-        certList.add(_signCrl);
-
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_signKP.getPrivate(), _signCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certsAndCrls);
-
-        CMSSignedData s = gen.generate(msg, true, BC);
-        SignerInformation origSigner = (SignerInformation)s.getSignerInfos().getSigners().toArray()[0];
-        SignerInformationStore counterSigners1 = gen.generateCounterSigners(origSigner, BC);
-        SignerInformationStore counterSigners2 = gen.generateCounterSigners(origSigner, BC);
-
-        SignerInformation signer1 = SignerInformation.addCounterSigners(origSigner, counterSigners1);
-        SignerInformation signer2 = SignerInformation.addCounterSigners(signer1, counterSigners2);
-
-        SignerInformationStore cs = signer2.getCounterSignatures();
-        Collection csSigners = cs.getSigners();
-        assertEquals(2, csSigners.size());
-
-        Iterator it = csSigners.iterator();
-        while (it.hasNext())
-        {
-            SignerInformation   cSigner = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(cSigner.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertNull(cSigner.getSignedAttributes().get(PKCSObjectIdentifiers.pkcs_9_at_contentType));
-            assertEquals(true, cSigner.verify(cert, BC));
-        }
-    }
-
-    private void rsaPSSTest(String digestName, String digestOID)
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello world!".getBytes());
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.ENCRYPTION_RSA_PSS, digestOID);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData s = gen.generate(CMSSignedDataGenerator.DATA, msg, false, BC, false);
-
-        //
-        // compute expected content digest
-        //
-        MessageDigest md = MessageDigest.getInstance(digestName, BC);
-
-        verifySignatures(s, md.digest("Hello world!".getBytes()));
-    }
-
-    private void subjectKeyIDTest(
-        KeyPair         signaturePair,
-        X509Certificate signatureCert,
-        String          digestAlgorithm)
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-        certList.add(signatureCert);
-        certList.add(_origCert);
-
-        certList.add(_signCrl);
-
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(signaturePair.getPrivate(), CMSTestUtil.createSubjectKeyId(signatureCert.getPublicKey()).getKeyIdentifier(), digestAlgorithm);
-
-        gen.addCertificatesAndCRLs(certsAndCrls);
-
-        CMSSignedData s = gen.generate(msg, true, BC);
-
-        assertEquals(3, s.getVersion());
-        
-        ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
-        ASN1InputStream      aIn = new ASN1InputStream(bIn);
-
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-
-        certsAndCrls = s.getCertificatesAndCRLs("Collection", BC);
-
-        SignerInformationStore  signers = s.getSignerInfos();
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertEquals(true, signer.verify(cert, BC));
-        }
-
-        //
-        // check for CRLs
-        //
-        Collection crls = certsAndCrls.getCRLs(null);
-
-        assertEquals(1, crls.size());
-
-        assertTrue(crls.contains(_signCrl));
-
-        //
-        // try using existing signer
-        //
-
-        gen = new CMSSignedDataGenerator();
-
-        gen.addSigners(s.getSignerInfos());
-
-        gen.addCertificatesAndCRLs(s.getCertificatesAndCRLs("Collection", BC));
-
-        s = gen.generate(msg, true, BC);
-
-        bIn = new ByteArrayInputStream(s.getEncoded());
-        aIn = new ASN1InputStream(bIn);
-
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-
-        certsAndCrls = s.getCertificatesAndCRLs("Collection", BC);
-
-        signers = s.getSignerInfos();
-        c = signers.getSigners();
-        it = c.iterator();
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertEquals(true, signer.verify(cert, BC));
-        }
-
-        checkSignerStoreReplacement(s, signers);
-    }
-
-    private void encapsulatedTest(
-        KeyPair         signaturePair, 
-        X509Certificate signatureCert,
-        String          digestAlgorithm)
-        throws Exception
-    {
-        List                certList = new ArrayList();
-        CMSProcessable      msg = new CMSProcessableByteArray("Hello World!".getBytes());
-    
-        certList.add(signatureCert);
-        certList.add(_origCert);
-
-        certList.add(_signCrl);
-
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-    
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-    
-        gen.addSigner(signaturePair.getPrivate(), signatureCert, digestAlgorithm);
-    
-        gen.addCertificatesAndCRLs(certsAndCrls);
-    
-        CMSSignedData s = gen.generate(msg, true, BC);
-    
-        ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
-        ASN1InputStream      aIn = new ASN1InputStream(bIn);
-        
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-    
-        certsAndCrls = s.getCertificatesAndCRLs("Collection", BC);
-    
-        SignerInformationStore  signers = s.getSignerInfos();
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-    
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-    
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-    
-            assertEquals(true, signer.verify(cert, BC));
-        }
-
-        //
-        // check for CRLs
-        //
-        Collection crls = certsAndCrls.getCRLs(null);
-
-        assertEquals(1, crls.size());
-
-        assertTrue(crls.contains(_signCrl));
-        
-        //
-        // try using existing signer
-        //
-        
-        gen = new CMSSignedDataGenerator();
-           
-        gen.addSigners(s.getSignerInfos());
-        
-        gen.addCertificatesAndCRLs(s.getCertificatesAndCRLs("Collection", BC));
-           
-        s = gen.generate(msg, true, BC);
-    
-        bIn = new ByteArrayInputStream(s.getEncoded());
-        aIn = new ASN1InputStream(bIn);
-    
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-    
-        certsAndCrls = s.getCertificatesAndCRLs("Collection", BC);
-    
-        signers = s.getSignerInfos();
-        c = signers.getSigners();
-        it = c.iterator();
-    
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-    
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-    
-            assertEquals(true, signer.verify(cert, BC));
-        }
-        
-        checkSignerStoreReplacement(s, signers);
-    }
-
-    //
-    // signerInformation store replacement test.
-    //
-    private void checkSignerStoreReplacement(
-        CMSSignedData orig, 
-        SignerInformationStore signers) 
-        throws Exception
-    {
-        CMSSignedData s = CMSSignedData.replaceSigners(orig, signers);
-        
-        CertStore certs = s.getCertificatesAndCRLs("Collection", BC);
-        
-        signers = s.getSignerInfos();
-        Collection c = signers.getSigners();
-        Iterator   it = c.iterator();
-    
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-    
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-    
-            assertEquals(true, signer.verify(cert, BC));
-        }
-    }
-    
-    public void testUnsortedAttributes()
-        throws Exception
-    {
-        CMSSignedData s = new CMSSignedData(new CMSProcessableByteArray(disorderedMessage), disorderedSet);
-
-        CertStore certs = s.getCertificatesAndCRLs("Collection", BC);
-
-        SignerInformationStore  signers = s.getSignerInfos();
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertEquals(true, signer.verify(cert, BC));
-        }
-    }
-    
-    public void testNullContentWithSigner()
-        throws Exception
-    {
-        List                certList = new ArrayList();
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData s = gen.generate(null, false, BC);
-
-        ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
-        ASN1InputStream      aIn = new ASN1InputStream(bIn);
-        
-        s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
-
-        verifySignatures(s);
-    }
-
-    public void testWithAttributeCertificate()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        CMSProcessable        msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-
-        certList.add(_signDsaCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        X509AttributeCertificate attrCert = CMSTestUtil.getAttributeCertificate();
-
-        X509Store store = X509Store.getInstance("AttributeCertificate/Collection",
-                                    new X509CollectionStoreParameters(Collections.singleton(attrCert)), BC);
-
-        gen.addAttributeCertificates(store);
-
-        CMSSignedData sd = gen.generate(msg, BC);
-
-        assertEquals(4, sd.getVersion());
-
-        store = sd.getAttributeCertificates("Collection", BC);
-
-        Collection coll = store.getMatches(null);
-
-        assertEquals(1, coll.size());
-
-        assertTrue(coll.contains(attrCert));
-        
-        //
-        // create new certstore
-        //
-        certList = new ArrayList();
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-
-        //
-        // replace certs
-        //
-        sd = CMSSignedData.replaceCertificatesAndCRLs(sd, certs);
-
-        verifySignatures(sd);
-    }
-
-    public void testCertStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        CMSProcessable        msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-
-        certList.add(_signDsaCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData sd = gen.generate(msg, BC);
-
-        //
-        // create new certstore
-        //
-        certList = new ArrayList();
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        //
-        // replace certs
-        //
-        sd = CMSSignedData.replaceCertificatesAndCRLs(sd, certs);
-
-        verifySignatures(sd);
-    }
-
-    public void testEncapsulatedCertStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        CMSProcessable        msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-
-        certList.add(_signDsaCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData sd = gen.generate(msg, true, BC);
-
-        //
-        // create new certstore
-        //
-        certList = new ArrayList();
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        //
-        // replace certs
-        //
-        sd = CMSSignedData.replaceCertificatesAndCRLs(sd, certs);
-
-        verifySignatures(sd);
-    }
-
-    public void testCertOrdering1()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        CMSProcessable        msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-        certList.add(_signDsaCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData sd = gen.generate(msg, true, BC);
-
-        certs = sd.getCertificatesAndCRLs("Collection", BC);
-        Iterator it = certs.getCertificates(null).iterator();
-
-        assertEquals(_origCert, it.next());
-        assertEquals(_signCert, it.next());
-        assertEquals(_signDsaCert, it.next());
-    }
-
-    public void testCertOrdering2()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        CMSProcessable        msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-        certList.add(_signCert);
-        certList.add(_signDsaCert);
-        certList.add(_origCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData sd = gen.generate(msg, true, BC);
-
-        certs = sd.getCertificatesAndCRLs("Collection", BC);
-        Iterator it = certs.getCertificates(null).iterator();
-
-        assertEquals(_signCert, it.next());
-        assertEquals(_signDsaCert, it.next());
-        assertEquals(_origCert, it.next());
-    }
-
-    public void testSignerStoreReplacement()
-        throws Exception
-    {
-        List                  certList = new ArrayList();
-        CMSProcessable        msg = new CMSProcessableByteArray("Hello World!".getBytes());
-
-        certList.add(_origCert);
-        certList.add(_signCert);
-
-        CertStore           certs = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), BC);
-
-        CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA1);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData original = gen.generate(msg, true, BC);
-
-        //
-        // create new Signer
-        //
-        gen = new CMSSignedDataGenerator();
-
-        gen.addSigner(_origKP.getPrivate(), _origCert, CMSSignedDataGenerator.DIGEST_SHA224);
-
-        gen.addCertificatesAndCRLs(certs);
-
-        CMSSignedData newSD = gen.generate(msg, true, BC);
-
-        //
-        // replace signer
-        //
-        CMSSignedData sd = CMSSignedData.replaceSigners(original, newSD.getSignerInfos());
-
-        SignerInformation signer = (SignerInformation)sd.getSignerInfos().getSigners().iterator().next();
-
-        assertEquals(CMSSignedDataGenerator.DIGEST_SHA224, signer.getDigestAlgOID());
-
-        // we use a parser here as it requires the digests to be correct in the digest set, if it
-        // isn't we'll get a NullPointerException
-        CMSSignedDataParser sp = new CMSSignedDataParser(sd.getEncoded());
-
-        sp.getSignedContent().drain();
-
-        verifySignatures(sp);
-    }
-
-    public void testEncapsulatedSamples()
-        throws Exception
-    {
-        testSample("PSSSignDataSHA1Enc.sig");
-        testSample("PSSSignDataSHA256Enc.sig");
-        testSample("PSSSignDataSHA512Enc.sig");
-    }
-    
-    public void testSamples()
-        throws Exception
-    {
-        testSample("PSSSignData.data", "PSSSignDataSHA1.sig");
-        testSample("PSSSignData.data", "PSSSignDataSHA256.sig");
-        testSample("PSSSignData.data", "PSSSignDataSHA512.sig");
-    }
-
-    public void testCounterSig()
-        throws Exception
-    {
-        CMSSignedData sig = new CMSSignedData(getInput("counterSig.p7m"));
-
-        SignerInformationStore ss = sig.getSignerInfos();
-        Collection signers = ss.getSigners();
-
-        SignerInformationStore cs = ((SignerInformation)signers.iterator().next()).getCounterSignatures();
-        Collection csSigners = cs.getSigners();
-        assertEquals(1, csSigners.size());
-
-        Iterator it = csSigners.iterator();
-        while (it.hasNext())
-        {
-            SignerInformation   cSigner = (SignerInformation)it.next();
-            Collection          certCollection = sig.getCertificatesAndCRLs("Collection", BC).getCertificates(selectorConverter.getCertSelector(cSigner.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertNull(cSigner.getSignedAttributes().get(PKCSObjectIdentifiers.pkcs_9_at_contentType));
-            assertEquals(true, cSigner.verify(cert, BC));
-        }
-        
-        verifySignatures(sig);
-    }
-
-    private void testSample(String sigName)
-        throws Exception
-    {
-        CMSSignedData sig = new CMSSignedData(getInput(sigName));
-
-        verifySignatures(sig);
-    }
-
-    private void testSample(String messageName, String sigName)
-        throws Exception
-    {
-        CMSSignedData sig = new CMSSignedData(new CMSProcessableByteArray(getInput(messageName)), getInput(sigName));
-
-        verifySignatures(sig);
-    }
-
-    private byte[] getInput(String name)
-        throws IOException
-    {
-        return Streams.readAll(getClass().getResourceAsStream(name));
-    }
-
-    public void testForMultipleCounterSignatures()
-        throws Exception
-    {
-        CMSSignedData sd = new CMSSignedData(xtraCounterSig);
-
-        for (Iterator sI = sd.getSignerInfos().getSigners().iterator(); sI.hasNext();)
-        {
-            SignerInformation sigI = (SignerInformation)sI.next();
-
-            SignerInformationStore counter = sigI.getCounterSignatures();
-            List                   sigs = new ArrayList(counter.getSigners());
-
-            assertEquals(2, sigs.size());
-        }
-    }
-
-    private void verifySignatures(CMSSignedDataParser sp)
-        throws Exception
-    {
-        CertStore               certs = sp.getCertificatesAndCRLs("Collection", BC);
-        SignerInformationStore  signers = sp.getSignerInfos();
-
-        Collection              c = signers.getSigners();
-        Iterator                it = c.iterator();
-
-        while (it.hasNext())
-        {
-            SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certs.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
-
-            Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
-
-            assertEquals(true, signer.verify(cert, BC));
-        }
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/test/SunProviderTest.java b/bcpkix/src/main/java/org/bouncycastle/cms/test/SunProviderTest.java
index 9412b99..3ec8c5a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/SunProviderTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/SunProviderTest.java
@@ -14,8 +14,6 @@
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.cert.CertStore;
-import java.security.cert.CollectionCertStoreParameters;
 import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -28,23 +26,37 @@
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.X509Name;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cms.CMSEnvelopedData;
 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
-import org.bouncycastle.cms.CMSProcessable;
 import org.bouncycastle.cms.CMSProcessableByteArray;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.cms.CMSSignedDataGenerator;
 import org.bouncycastle.cms.CMSSignedDataParser;
 import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
+import org.bouncycastle.cms.CMSTypedData;
 import org.bouncycastle.cms.CMSTypedStream;
 import org.bouncycastle.cms.RecipientInformation;
 import org.bouncycastle.cms.RecipientInformationStore;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
 import org.bouncycastle.cms.jcajce.JcaX509CertSelectorConverter;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.CollectionStore;
+import org.bouncycastle.util.Store;
 import org.bouncycastle.x509.X509V3CertificateGenerator;
 
 public class SunProviderTest
@@ -73,27 +85,26 @@
         throws Exception
     {
         List certList = new ArrayList();
-        CMSProcessable msg = new CMSProcessableByteArray(TEST_MESSAGE.getBytes());
+        CMSTypedData msg = new CMSProcessableByteArray(TEST_MESSAGE.getBytes());
 
-        certList.add(keyCert);
+        certList.add(new X509CertificateHolder(keyCert.getEncoded()));
 
-        CertStore certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), "SUN");
+        DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().build();
 
         CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
 
-        gen.addSigner(keyPair.getPrivate(), keyCert, CMSSignedDataGenerator.DIGEST_SHA1);
+        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("SHA1withRSA").setProvider("SunRsaSign").build(keyPair.getPrivate()), keyCert));
 
-        gen.addCertificatesAndCRLs(certsAndCrls);
+        gen.addCertificates(new CollectionStore(certList));
 
-        CMSSignedData s = gen.generate(msg, true, "SunRsaSign");
+        CMSSignedData s = gen.generate(msg, true);
 
         ByteArrayInputStream bIn = new ByteArrayInputStream(s.getEncoded());
         ASN1InputStream aIn = new ASN1InputStream(bIn);
 
         s = new CMSSignedData(ContentInfo.getInstance(aIn.readObject()));
 
-        certsAndCrls = s.getCertificatesAndCRLs("Collection", "SUN");
+        Store certsAndCrls = s.getCertificates();
 
         SignerInformationStore signers = s.getSignerInfos();
         Collection c = signers.getSigners();
@@ -102,12 +113,12 @@
         while (it.hasNext())
         {
             SignerInformation signer = (SignerInformation)it.next();
-            Collection          certCollection = certsAndCrls.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            Collection          certCollection = certsAndCrls.getMatches(signer.getSID());
 
             Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
+            X509Certificate cert = new JcaX509CertificateConverter().getCertificate((X509CertificateHolder)certIt.next());
 
-            assertEquals(true, signer.verify(cert, "SunRsaSign"));
+            assertEquals(true, signer.verify(new JcaSignerInfoVerifierBuilder(new JcaDigestCalculatorProviderBuilder().build()).setProvider("SunRsaSign").build(cert)));
         }
     }
 
@@ -117,16 +128,15 @@
         List                  certList = new ArrayList();
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
 
-        certList.add(keyCert);
+        certList.add(new X509CertificateHolder(keyCert.getEncoded()));
 
-        CertStore           certsAndCrls = CertStore.getInstance("Collection",
-                        new CollectionCertStoreParameters(certList), "SUN");
+        DigestCalculatorProvider digCalcProv = new JcaDigestCalculatorProviderBuilder().build();
 
         CMSSignedDataStreamGenerator gen = new CMSSignedDataStreamGenerator();
 
-        gen.addSigner(keyPair.getPrivate(), keyCert, CMSSignedDataStreamGenerator.DIGEST_SHA1, "SunRsaSign");
+        gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(digCalcProv).build(new JcaContentSignerBuilder("SHA1withRSA").setProvider("SunRsaSign").build(keyPair.getPrivate()), keyCert));
 
-        gen.addCertificatesAndCRLs(certsAndCrls);
+        gen.addCertificates(new CollectionStore(certList));
 
         OutputStream sigOut = gen.open(bOut);
 
@@ -134,7 +144,7 @@
 
         sigOut.close();
 
-        CMSSignedDataParser sp = new CMSSignedDataParser(
+        CMSSignedDataParser sp = new CMSSignedDataParser(digCalcProv,
                 new CMSTypedStream(new ByteArrayInputStream(TEST_MESSAGE.getBytes())), bOut.toByteArray());
 
         sp.getSignedContent().drain();
@@ -145,7 +155,7 @@
         MessageDigest md = MessageDigest.getInstance("SHA1", "SUN");
 
         byte[]                  contentDigest = md.digest(TEST_MESSAGE.getBytes());
-        CertStore               certStore = sp.getCertificatesAndCRLs("Collection", "SUN");
+        Store                   certStore = sp.getCertificates();
         SignerInformationStore  signers = sp.getSignerInfos();
 
         Collection              c = signers.getSigners();
@@ -154,12 +164,12 @@
         while (it.hasNext())
         {
             SignerInformation   signer = (SignerInformation)it.next();
-            Collection          certCollection = certStore.getCertificates(selectorConverter.getCertSelector(signer.getSID()));
+            Collection          certCollection = certStore.getMatches(signer.getSID());
 
             Iterator        certIt = certCollection.iterator();
-            X509Certificate cert = (X509Certificate)certIt.next();
+            X509CertificateHolder cert = (X509CertificateHolder)certIt.next();
 
-            assertEquals(true, signer.verify(cert, "SunRsaSign"));
+            assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("SunRsaSign").build(new JcaX509CertificateConverter().getCertificate(cert))));
 
             if (contentDigest != null)
             {
@@ -199,11 +209,11 @@
 
         CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
 
-        edGen.addKeyTransRecipient(keyCert);
+        edGen.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(keyCert).setProvider("SunJCE"));
 
         CMSEnvelopedData ed = edGen.generate(
                                 new CMSProcessableByteArray(data),
-                                algorithm, "SunJCE");
+                                new JceCMSContentEncryptorBuilder(new ASN1ObjectIdentifier(algorithm)).setProvider("SunJCE").build());
 
         RecipientInformationStore recipients = ed.getRecipientInfos();
 
@@ -222,7 +232,7 @@
 
             assertEquals(recipient.getKeyEncryptionAlgOID(), PKCSObjectIdentifiers.rsaEncryption.getId());
 
-            byte[] recData = recipient.getContent(keyPair.getPrivate(), "SunJCE");
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(keyPair.getPrivate()).setProvider("SunJCE"));
 
             assertEquals(true, Arrays.equals(data, recData));
         }
diff --git a/bcpkix/src/main/java/org/bouncycastle/dvcs/package.html b/bcpkix/src/main/java/org/bouncycastle/dvcs/package.html
deleted file mode 100644
index aecbd70..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/dvcs/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for dealing "Internet X.509 Public Key Infrastructure Data Validation and Certification Server Protocols" - RFC 3029.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/eac/package.html b/bcpkix/src/main/java/org/bouncycastle/eac/package.html
deleted file mode 100644
index 97c41fa..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/eac/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Base classes Extended Access Control (EAC) Certificates as described in "Technical Guideline, Advanced Security Mechanisms for Machine Readable Travel Documents, Extended Access Control (EAC), Version 1.0.1, BSI 2006".
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/mozilla/package.html b/bcpkix/src/main/java/org/bouncycastle/mozilla/package.html
deleted file mode 100644
index dd2203e..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/mozilla/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support class for mozilla signed public key and challenge.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMReader.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMReader.java
deleted file mode 100644
index b11ae12..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMReader.java
+++ /dev/null
@@ -1,1023 +0,0 @@
-package org.bouncycastle.openssl;
-
-import java.io.ByteArrayInputStream;
-import java.io.IOException;
-import java.io.Reader;
-import java.security.AlgorithmParameters;
-import java.security.Key;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.Provider;
-import java.security.PublicKey;
-import java.security.Security;
-import java.security.cert.CertificateFactory;
-import java.security.spec.AlgorithmParameterSpec;
-import java.security.spec.DSAPrivateKeySpec;
-import java.security.spec.DSAPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
-import java.security.spec.KeySpec;
-import java.security.spec.PKCS8EncodedKeySpec;
-import java.security.spec.RSAPrivateCrtKeySpec;
-import java.security.spec.RSAPublicKeySpec;
-import java.security.spec.X509EncodedKeySpec;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-
-import javax.crypto.Cipher;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.PBEKeySpec;
-import javax.crypto.spec.PBEParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERInteger;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.cms.ContentInfo;
-import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
-import org.bouncycastle.asn1.pkcs.EncryptionScheme;
-import org.bouncycastle.asn1.pkcs.KeyDerivationFunc;
-import org.bouncycastle.asn1.pkcs.PBEParameter;
-import org.bouncycastle.asn1.pkcs.PBES2Parameters;
-import org.bouncycastle.asn1.pkcs.PBKDF2Params;
-import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
-import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.asn1.pkcs.RSAPublicKey;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
-import org.bouncycastle.crypto.PBEParametersGenerator;
-import org.bouncycastle.crypto.generators.OpenSSLPBEParametersGenerator;
-import org.bouncycastle.crypto.generators.PKCS5S2ParametersGenerator;
-import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.jce.ECNamedCurveTable;
-import org.bouncycastle.jce.PKCS10CertificationRequest;
-import org.bouncycastle.util.encoders.Hex;
-import org.bouncycastle.util.io.pem.PemHeader;
-import org.bouncycastle.util.io.pem.PemObject;
-import org.bouncycastle.util.io.pem.PemObjectParser;
-import org.bouncycastle.util.io.pem.PemReader;
-import org.bouncycastle.x509.X509V2AttributeCertificate;
-
-/**
- * Class for reading OpenSSL PEM encoded streams containing
- * X509 certificates, PKCS8 encoded keys and PKCS7 objects.
- * <p>
- * In the case of PKCS7 objects the reader will return a CMS ContentInfo object. Keys and
- * Certificates will be returned using the appropriate java.security type (KeyPair, PublicKey, X509Certificate,
- * or X509CRL). In the case of a Certificate Request a PKCS10CertificationRequest will be returned.
- * </p>
- *
- * @deprecated use PEMParser
- */
-public class PEMReader
-    extends PemReader
-{
-    private final Map parsers = new HashMap();
-
-    private PasswordFinder pFinder;
-
-
-    /**
-     * Create a new PEMReader
-     *
-     * @param reader the Reader
-     * @deprecated use PEMParser
-     */
-    public PEMReader(
-        Reader reader)
-    {
-        this(reader, null, "BC");
-    }
-
-    /**
-     * Create a new PEMReader with a password finder
-     *
-     * @param reader  the Reader
-     * @param pFinder the password finder
-     * @deprecated use PEMParser
-     */
-    public PEMReader(
-        Reader reader,
-        PasswordFinder pFinder)
-    {
-        this(reader, pFinder, "BC");
-    }
-
-    /**
-     * Create a new PEMReader with a password finder
-     *
-     * @param reader   the Reader
-     * @param pFinder  the password finder
-     * @param provider the cryptography provider to use
-     * @deprecated use PEMParser
-     */
-    public PEMReader(
-        Reader reader,
-        PasswordFinder pFinder,
-        String provider)
-    {
-        this(reader, pFinder, provider, provider);
-    }
-
-    /**
-     * Create a new PEMReader with a password finder and differing providers for secret and public key
-     * operations.
-     *
-     * @param reader       the Reader
-     * @param pFinder      the password finder
-     * @param symProvider  provider to use for symmetric operations
-     * @param asymProvider provider to use for asymmetric (public/private key) operations
-     * @deprecated use PEMParser
-     */
-    public PEMReader(
-        Reader reader,
-        PasswordFinder pFinder,
-        String symProvider,
-        String asymProvider)
-    {
-        super(reader);
-
-        this.pFinder = pFinder;
-
-        parsers.put("CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
-        parsers.put("NEW CERTIFICATE REQUEST", new PKCS10CertificationRequestParser());
-        parsers.put("CERTIFICATE", new X509CertificateParser(asymProvider));
-        parsers.put("X509 CERTIFICATE", new X509CertificateParser(asymProvider));
-        parsers.put("X509 CRL", new X509CRLParser(asymProvider));
-        parsers.put("PKCS7", new PKCS7Parser());
-        parsers.put("ATTRIBUTE CERTIFICATE", new X509AttributeCertificateParser());
-        parsers.put("EC PARAMETERS", new ECNamedCurveSpecParser());
-        parsers.put("PUBLIC KEY", new PublicKeyParser(asymProvider));
-        parsers.put("RSA PUBLIC KEY", new RSAPublicKeyParser(asymProvider));
-        parsers.put("RSA PRIVATE KEY", new RSAKeyPairParser(symProvider, asymProvider));
-        parsers.put("DSA PRIVATE KEY", new DSAKeyPairParser(symProvider, asymProvider));
-        parsers.put("EC PRIVATE KEY", new ECDSAKeyPairParser(symProvider, asymProvider));
-        parsers.put("ENCRYPTED PRIVATE KEY", new EncryptedPrivateKeyParser(symProvider, asymProvider));
-        parsers.put("PRIVATE KEY", new PrivateKeyParser(asymProvider));
-    }
-
-    public Object readObject()
-        throws IOException
-    {
-        PemObject obj = readPemObject();
-
-        if (obj != null)
-        {
-            String type = obj.getType();
-            if (parsers.containsKey(type))
-            {
-                return ((PemObjectParser)parsers.get(type)).parseObject(obj);
-            }
-            else
-            {
-                throw new IOException("unrecognised object: " + type);
-            }
-        }
-
-        return null;
-    }
-
-    private abstract class KeyPairParser
-        implements PemObjectParser
-    {
-        protected String symProvider;
-
-        public KeyPairParser(String symProvider)
-        {
-            this.symProvider = symProvider;
-        }
-
-        /**
-         * Read a Key Pair
-         */
-        protected ASN1Sequence readKeyPair(
-            PemObject obj)
-            throws IOException
-        {
-            boolean isEncrypted = false;
-            String dekInfo = null;
-            List headers = obj.getHeaders();
-
-            for (Iterator it = headers.iterator(); it.hasNext(); )
-            {
-                PemHeader hdr = (PemHeader)it.next();
-
-                if (hdr.getName().equals("Proc-Type") && hdr.getValue().equals("4,ENCRYPTED"))
-                {
-                    isEncrypted = true;
-                }
-                else if (hdr.getName().equals("DEK-Info"))
-                {
-                    dekInfo = hdr.getValue();
-                }
-            }
-
-            //
-            // extract the key
-            //
-            byte[] keyBytes = obj.getContent();
-
-            if (isEncrypted)
-            {
-                if (pFinder == null)
-                {
-                    throw new PasswordException("No password finder specified, but a password is required");
-                }
-
-                char[] password = pFinder.getPassword();
-
-                if (password == null)
-                {
-                    throw new PasswordException("Password is null, but a password is required");
-                }
-
-                StringTokenizer tknz = new StringTokenizer(dekInfo, ",");
-                String dekAlgName = tknz.nextToken();
-                byte[] iv = Hex.decode(tknz.nextToken());
-
-                keyBytes = crypt(false, symProvider, keyBytes, password, dekAlgName, iv);
-            }
-
-            try
-            {
-                return ASN1Sequence.getInstance(ASN1Primitive.fromByteArray(keyBytes));
-            }
-            catch (IOException e)
-            {
-                if (isEncrypted)
-                {
-                    throw new PEMException("exception decoding - please check password and data.", e);
-                }
-                else
-                {
-                    throw new PEMException(e.getMessage(), e);
-                }
-            }
-            catch (IllegalArgumentException e)
-            {
-                if (isEncrypted)
-                {
-                    throw new PEMException("exception decoding - please check password and data.", e);
-                }
-                else
-                {
-                    throw new PEMException(e.getMessage(), e);
-                }
-            }
-        }
-    }
-
-    private class DSAKeyPairParser
-        extends KeyPairParser
-    {
-        private String asymProvider;
-
-        public DSAKeyPairParser(String symProvider, String asymProvider)
-        {
-            super(symProvider);
-
-            this.asymProvider = asymProvider;
-        }
-
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                ASN1Sequence seq = readKeyPair(obj);
-
-                if (seq.size() != 6)
-                {
-                    throw new PEMException("malformed sequence in DSA private key");
-                }
-
-                //            DERInteger              v = (DERInteger)seq.getObjectAt(0);
-                DERInteger p = (DERInteger)seq.getObjectAt(1);
-                DERInteger q = (DERInteger)seq.getObjectAt(2);
-                DERInteger g = (DERInteger)seq.getObjectAt(3);
-                DERInteger y = (DERInteger)seq.getObjectAt(4);
-                DERInteger x = (DERInteger)seq.getObjectAt(5);
-
-                DSAPrivateKeySpec privSpec = new DSAPrivateKeySpec(
-                    x.getValue(), p.getValue(),
-                    q.getValue(), g.getValue());
-                DSAPublicKeySpec pubSpec = new DSAPublicKeySpec(
-                    y.getValue(), p.getValue(),
-                    q.getValue(), g.getValue());
-
-                KeyFactory fact = KeyFactory.getInstance("DSA", asymProvider);
-
-                return new KeyPair(
-                    fact.generatePublic(pubSpec),
-                    fact.generatePrivate(privSpec));
-            }
-            catch (IOException e)
-            {
-                throw e;
-            }
-            catch (Exception e)
-            {
-                throw new PEMException(
-                    "problem creating DSA private key: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class ECDSAKeyPairParser
-        extends KeyPairParser
-    {
-        private String asymProvider;
-
-        public ECDSAKeyPairParser(String symProvider, String asymProvider)
-        {
-            super(symProvider);
-
-            this.asymProvider = asymProvider;
-        }
-
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                ASN1Sequence seq = readKeyPair(obj);
-
-                org.bouncycastle.asn1.sec.ECPrivateKey pKey = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(seq);
-                AlgorithmIdentifier algId = new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, pKey.getParameters());
-                PrivateKeyInfo privInfo = new PrivateKeyInfo(algId, pKey);
-                SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());
-
-                PKCS8EncodedKeySpec privSpec = new PKCS8EncodedKeySpec(privInfo.getEncoded());
-                X509EncodedKeySpec pubSpec = new X509EncodedKeySpec(pubInfo.getEncoded());
-
-
-                KeyFactory fact = KeyFactory.getInstance("ECDSA", asymProvider);
-
-
-                return new KeyPair(
-                    fact.generatePublic(pubSpec),
-                    fact.generatePrivate(privSpec));
-            }
-            catch (IOException e)
-            {
-                throw e;
-            }
-            catch (Exception e)
-            {
-                throw new PEMException(
-                    "problem creating EC private key: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class RSAKeyPairParser
-        extends KeyPairParser
-    {
-        private String asymProvider;
-
-        public RSAKeyPairParser(String symProvider, String asymProvider)
-        {
-            super(symProvider);
-
-            this.asymProvider = asymProvider;
-        }
-
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                ASN1Sequence seq = readKeyPair(obj);
-
-                if (seq.size() != 9)
-                {
-                    throw new PEMException("malformed sequence in RSA private key");
-                }
-
-                org.bouncycastle.asn1.pkcs.RSAPrivateKey keyStruct = org.bouncycastle.asn1.pkcs.RSAPrivateKey.getInstance(seq);
-
-                RSAPublicKeySpec pubSpec = new RSAPublicKeySpec(
-                    keyStruct.getModulus(), keyStruct.getPublicExponent());
-                RSAPrivateCrtKeySpec privSpec = new RSAPrivateCrtKeySpec(
-                    keyStruct.getModulus(), keyStruct.getPublicExponent(), keyStruct.getPrivateExponent(),
-                    keyStruct.getPrime1(), keyStruct.getPrime2(),
-                    keyStruct.getExponent1(), keyStruct.getExponent2(),
-                    keyStruct.getCoefficient());
-
-                KeyFactory fact = KeyFactory.getInstance("RSA", asymProvider);
-
-                return new KeyPair(
-                    fact.generatePublic(pubSpec),
-                    fact.generatePrivate(privSpec));
-            }
-            catch (IOException e)
-            {
-                throw e;
-            }
-            catch (Exception e)
-            {
-                throw new PEMException(
-                    "problem creating RSA private key: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class PublicKeyParser
-        implements PemObjectParser
-    {
-        private String provider;
-
-        public PublicKeyParser(String provider)
-        {
-            this.provider = provider;
-        }
-
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            KeySpec keySpec = new X509EncodedKeySpec(obj.getContent());
-            String[] algorithms = {"DSA", "RSA"};
-            for (int i = 0; i < algorithms.length; i++)
-            {
-                try
-                {
-                    KeyFactory keyFact = KeyFactory.getInstance(algorithms[i], provider);
-                    PublicKey pubKey = keyFact.generatePublic(keySpec);
-
-                    return pubKey;
-                }
-                catch (NoSuchAlgorithmException e)
-                {
-                    // ignore
-                }
-                catch (InvalidKeySpecException e)
-                {
-                    // ignore
-                }
-                catch (NoSuchProviderException e)
-                {
-                    throw new RuntimeException("can't find provider " + provider);
-                }
-            }
-
-            return null;
-        }
-    }
-
-    private class RSAPublicKeyParser
-        implements PemObjectParser
-    {
-        private String provider;
-
-        public RSAPublicKeyParser(String provider)
-        {
-            this.provider = provider;
-        }
-
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                ASN1InputStream ais = new ASN1InputStream(obj.getContent());
-                Object asnObject = ais.readObject();
-                ASN1Sequence sequence = (ASN1Sequence)asnObject;
-                RSAPublicKey rsaPubStructure = RSAPublicKey.getInstance(sequence);
-                RSAPublicKeySpec keySpec = new RSAPublicKeySpec(
-                    rsaPubStructure.getModulus(),
-                    rsaPubStructure.getPublicExponent());
-
-
-                KeyFactory keyFact = KeyFactory.getInstance("RSA", provider);
-
-                return keyFact.generatePublic(keySpec);
-            }
-            catch (IOException e)
-            {
-                throw e;
-            }
-            catch (NoSuchProviderException e)
-            {
-                throw new IOException("can't find provider " + provider);
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem extracting key: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class X509CertificateParser
-        implements PemObjectParser
-    {
-        private String provider;
-
-        public X509CertificateParser(String provider)
-        {
-            this.provider = provider;
-        }
-
-        /**
-         * Reads in a X509Certificate.
-         *
-         * @return the X509Certificate
-         * @throws IOException if an I/O error occured
-         */
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            ByteArrayInputStream bIn = new ByteArrayInputStream(obj.getContent());
-
-            try
-            {
-                CertificateFactory certFact
-                    = CertificateFactory.getInstance("X.509", provider);
-
-                return certFact.generateCertificate(bIn);
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem parsing cert: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class X509CRLParser
-        implements PemObjectParser
-    {
-        private String provider;
-
-        public X509CRLParser(String provider)
-        {
-            this.provider = provider;
-        }
-
-        /**
-         * Reads in a X509CRL.
-         *
-         * @return the X509Certificate
-         * @throws IOException if an I/O error occured
-         */
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            ByteArrayInputStream bIn = new ByteArrayInputStream(obj.getContent());
-
-            try
-            {
-                CertificateFactory certFact
-                    = CertificateFactory.getInstance("X.509", provider);
-
-                return certFact.generateCRL(bIn);
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem parsing cert: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class PKCS10CertificationRequestParser
-        implements PemObjectParser
-    {
-        /**
-         * Reads in a PKCS10 certification request.
-         *
-         * @return the certificate request.
-         * @throws IOException if an I/O error occured
-         */
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                return new PKCS10CertificationRequest(obj.getContent());
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem parsing certrequest: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class PKCS7Parser
-        implements PemObjectParser
-    {
-        /**
-         * Reads in a PKCS7 object. This returns a ContentInfo object suitable for use with the CMS
-         * API.
-         *
-         * @return the X509Certificate
-         * @throws IOException if an I/O error occured
-         */
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                ASN1InputStream aIn = new ASN1InputStream(obj.getContent());
-
-                return ContentInfo.getInstance(aIn.readObject());
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem parsing PKCS7 object: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class X509AttributeCertificateParser
-        implements PemObjectParser
-    {
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            return new X509V2AttributeCertificate(obj.getContent());
-        }
-    }
-
-    private class ECNamedCurveSpecParser
-        implements PemObjectParser
-    {
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                DERObjectIdentifier oid = (DERObjectIdentifier)ASN1Primitive.fromByteArray(obj.getContent());
-
-                Object params = ECNamedCurveTable.getParameterSpec(oid.getId());
-
-                if (params == null)
-                {
-                    throw new IOException("object ID not found in EC curve table");
-                }
-
-                return params;
-            }
-            catch (IOException e)
-            {
-                throw e;
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("exception extracting EC named curve: " + e.toString());
-            }
-        }
-    }
-
-    private class EncryptedPrivateKeyParser
-        implements PemObjectParser
-    {
-        private String symProvider;
-        private String asymProvider;
-
-        public EncryptedPrivateKeyParser(String symProvider, String asymProvider)
-        {
-            this.symProvider = symProvider;
-            this.asymProvider = asymProvider;
-        }
-
-        /**
-         * Reads in a X509CRL.
-         *
-         * @return the X509Certificate
-         * @throws IOException if an I/O error occured
-         */
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                EncryptedPrivateKeyInfo info = EncryptedPrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(obj.getContent()));
-                AlgorithmIdentifier algId = info.getEncryptionAlgorithm();
-
-                if (pFinder == null)
-                {
-                    throw new PEMException("no PasswordFinder specified");
-                }
-
-                if (PEMUtilities.isPKCS5Scheme2(algId.getAlgorithm()))
-                {
-                    PBES2Parameters params = PBES2Parameters.getInstance(algId.getParameters());
-                    KeyDerivationFunc func = params.getKeyDerivationFunc();
-                    EncryptionScheme scheme = params.getEncryptionScheme();
-                    PBKDF2Params defParams = (PBKDF2Params)func.getParameters();
-
-                    int iterationCount = defParams.getIterationCount().intValue();
-                    byte[] salt = defParams.getSalt();
-
-                    String algorithm = scheme.getAlgorithm().getId();
-
-                    SecretKey key = generateSecretKeyForPKCS5Scheme2(algorithm, pFinder.getPassword(), salt, iterationCount);
-
-                    Cipher cipher = Cipher.getInstance(algorithm, symProvider);
-                    AlgorithmParameters algParams = AlgorithmParameters.getInstance(algorithm, symProvider);
-
-                    algParams.init(scheme.getParameters().toASN1Primitive().getEncoded());
-
-                    cipher.init(Cipher.DECRYPT_MODE, key, algParams);
-
-                    PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(cipher.doFinal(info.getEncryptedData())));
-                    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pInfo.getEncoded());
-
-                    KeyFactory keyFact = KeyFactory.getInstance(pInfo.getPrivateKeyAlgorithm().getAlgorithm().getId(), asymProvider);
-
-                    return keyFact.generatePrivate(keySpec);
-                }
-                else if (PEMUtilities.isPKCS12(algId.getAlgorithm()))
-                {
-                    PKCS12PBEParams params = PKCS12PBEParams.getInstance(algId.getParameters());
-                    String algorithm = algId.getAlgorithm().getId();
-                    PBEKeySpec pbeSpec = new PBEKeySpec(pFinder.getPassword());
-
-                    SecretKeyFactory secKeyFact = SecretKeyFactory.getInstance(algorithm, symProvider);
-                    PBEParameterSpec defParams = new PBEParameterSpec(params.getIV(), params.getIterations().intValue());
-
-                    Cipher cipher = Cipher.getInstance(algorithm, symProvider);
-
-                    cipher.init(Cipher.DECRYPT_MODE, secKeyFact.generateSecret(pbeSpec), defParams);
-
-                    PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(cipher.doFinal(info.getEncryptedData())));
-                    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pInfo.getEncoded());
-
-                    KeyFactory keyFact = KeyFactory.getInstance(pInfo.getPrivateKeyAlgorithm().getAlgorithm().getId(), asymProvider);
-
-                    return keyFact.generatePrivate(keySpec);
-                }
-                else if (PEMUtilities.isPKCS5Scheme1(algId.getAlgorithm()))
-                {
-                    PBEParameter params = PBEParameter.getInstance(algId.getParameters());
-                    String algorithm = algId.getAlgorithm().getId();
-                    PBEKeySpec pbeSpec = new PBEKeySpec(pFinder.getPassword());
-
-                    SecretKeyFactory secKeyFact = SecretKeyFactory.getInstance(algorithm, symProvider);
-                    PBEParameterSpec defParams = new PBEParameterSpec(params.getSalt(), params.getIterationCount().intValue());
-
-                    Cipher cipher = Cipher.getInstance(algorithm, symProvider);
-
-                    cipher.init(Cipher.DECRYPT_MODE, secKeyFact.generateSecret(pbeSpec), defParams);
-
-                    PrivateKeyInfo pInfo = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(cipher.doFinal(info.getEncryptedData())));
-                    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(pInfo.getEncoded());
-
-                    KeyFactory keyFact = KeyFactory.getInstance(pInfo.getPrivateKeyAlgorithm().getAlgorithm().getId(), asymProvider);
-
-                    return keyFact.generatePrivate(keySpec);
-                }
-                else
-                {
-                    throw new PEMException("Unknown algorithm: " + algId.getAlgorithm());
-                }
-            }
-            catch (IOException e)
-            {
-                throw e;
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem parsing ENCRYPTED PRIVATE KEY: " + e.toString(), e);
-            }
-        }
-    }
-
-    private class PrivateKeyParser
-        implements PemObjectParser
-    {
-        private String provider;
-
-        public PrivateKeyParser(String provider)
-        {
-            this.provider = provider;
-        }
-
-        public Object parseObject(PemObject obj)
-            throws IOException
-        {
-            try
-            {
-                PrivateKeyInfo info = PrivateKeyInfo.getInstance(ASN1Primitive.fromByteArray(obj.getContent()));
-                PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(obj.getContent());
-
-                KeyFactory keyFact = KeyFactory.getInstance(info.getPrivateKeyAlgorithm().getAlgorithm().getId(), provider);
-
-                return keyFact.generatePrivate(keySpec);
-            }
-            catch (Exception e)
-            {
-                throw new PEMException("problem parsing PRIVATE KEY: " + e.toString(), e);
-            }
-        }
-    }
-
-    static byte[] crypt(
-        boolean encrypt,
-        String provider,
-        byte[] bytes,
-        char[] password,
-        String dekAlgName,
-        byte[] iv)
-        throws IOException
-    {
-        Provider prov = null;
-        if (provider != null)
-        {
-            prov = Security.getProvider(provider);
-            if (prov == null)
-            {
-                throw new EncryptionException("cannot find provider: " + provider);
-            }
-        }
-
-        return crypt(encrypt, prov, bytes, password, dekAlgName, iv);
-    }
-
-    static byte[] crypt(
-        boolean encrypt,
-        Provider provider,
-        byte[] bytes,
-        char[] password,
-        String dekAlgName,
-        byte[] iv)
-        throws IOException
-    {
-        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
-        String alg;
-        String blockMode = "CBC";
-        String padding = "PKCS5Padding";
-        Key sKey;
-
-        // Figure out block mode and padding.
-        if (dekAlgName.endsWith("-CFB"))
-        {
-            blockMode = "CFB";
-            padding = "NoPadding";
-        }
-        if (dekAlgName.endsWith("-ECB") ||
-            "DES-EDE".equals(dekAlgName) ||
-            "DES-EDE3".equals(dekAlgName))
-        {
-            // ECB is actually the default (though seldom used) when OpenSSL
-            // uses DES-EDE (des2) or DES-EDE3 (des3).
-            blockMode = "ECB";
-            paramSpec = null;
-        }
-        if (dekAlgName.endsWith("-OFB"))
-        {
-            blockMode = "OFB";
-            padding = "NoPadding";
-        }
-
-
-        // Figure out algorithm and key size.
-        if (dekAlgName.startsWith("DES-EDE"))
-        {
-            alg = "DESede";
-            // "DES-EDE" is actually des2 in OpenSSL-speak!
-            // "DES-EDE3" is des3.
-            boolean des2 = !dekAlgName.startsWith("DES-EDE3");
-            sKey = getKey(password, alg, 24, iv, des2);
-        }
-        else if (dekAlgName.startsWith("DES-"))
-        {
-            alg = "DES";
-            sKey = getKey(password, alg, 8, iv);
-        }
-        else if (dekAlgName.startsWith("BF-"))
-        {
-            alg = "Blowfish";
-            sKey = getKey(password, alg, 16, iv);
-        }
-        else if (dekAlgName.startsWith("RC2-"))
-        {
-            alg = "RC2";
-            int keyBits = 128;
-            if (dekAlgName.startsWith("RC2-40-"))
-            {
-                keyBits = 40;
-            }
-            else if (dekAlgName.startsWith("RC2-64-"))
-            {
-                keyBits = 64;
-            }
-            sKey = getKey(password, alg, keyBits / 8, iv);
-            if (paramSpec == null) // ECB block mode
-            {
-                paramSpec = new RC2ParameterSpec(keyBits);
-            }
-            else
-            {
-                paramSpec = new RC2ParameterSpec(keyBits, iv);
-            }
-        }
-        else if (dekAlgName.startsWith("AES-"))
-        {
-            alg = "AES";
-            byte[] salt = iv;
-            if (salt.length > 8)
-            {
-                salt = new byte[8];
-                System.arraycopy(iv, 0, salt, 0, 8);
-            }
-
-            int keyBits;
-            if (dekAlgName.startsWith("AES-128-"))
-            {
-                keyBits = 128;
-            }
-            else if (dekAlgName.startsWith("AES-192-"))
-            {
-                keyBits = 192;
-            }
-            else if (dekAlgName.startsWith("AES-256-"))
-            {
-                keyBits = 256;
-            }
-            else
-            {
-                throw new EncryptionException("unknown AES encryption with private key");
-            }
-            sKey = getKey(password, "AES", keyBits / 8, salt);
-        }
-        else
-        {
-            throw new EncryptionException("unknown encryption with private key");
-        }
-
-        String transformation = alg + "/" + blockMode + "/" + padding;
-
-        try
-        {
-            Cipher c = Cipher.getInstance(transformation, provider);
-            int mode = encrypt ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
-
-            if (paramSpec == null) // ECB block mode
-            {
-                c.init(mode, sKey);
-            }
-            else
-            {
-                c.init(mode, sKey, paramSpec);
-            }
-            return c.doFinal(bytes);
-        }
-        catch (Exception e)
-        {
-            throw new EncryptionException("exception using cipher - please check password and data.", e);
-        }
-    }
-
-    private static SecretKey getKey(
-        char[] password,
-        String algorithm,
-        int keyLength,
-        byte[] salt)
-    {
-        return getKey(password, algorithm, keyLength, salt, false);
-    }
-
-    private static SecretKey getKey(
-        char[] password,
-        String algorithm,
-        int keyLength,
-        byte[] salt,
-        boolean des2)
-    {
-        OpenSSLPBEParametersGenerator pGen = new OpenSSLPBEParametersGenerator();
-
-        pGen.init(PBEParametersGenerator.PKCS5PasswordToBytes(password), salt);
-
-        KeyParameter keyParam;
-        keyParam = (KeyParameter)pGen.generateDerivedParameters(keyLength * 8);
-        byte[] key = keyParam.getKey();
-        if (des2 && key.length >= 24)
-        {
-            // For DES2, we must copy first 8 bytes into the last 8 bytes.
-            System.arraycopy(key, 0, key, 16, 8);
-        }
-        return new javax.crypto.spec.SecretKeySpec(key, algorithm);
-    }
-
-
-    public static SecretKey generateSecretKeyForPKCS5Scheme2(String algorithm, char[] password, byte[] salt, int iterationCount)
-    {
-        PBEParametersGenerator generator = new PKCS5S2ParametersGenerator();
-
-        generator.init(
-            PBEParametersGenerator.PKCS5PasswordToBytes(password),
-            salt,
-            iterationCount);
-
-        return new SecretKeySpec(((KeyParameter)generator.generateDerivedParameters(PEMUtilities.getKeySize(algorithm))).getKey(), algorithm);
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMWriter.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMWriter.java
index c9ef265..e46c836 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMWriter.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMWriter.java
@@ -2,10 +2,8 @@
 
 import java.io.IOException;
 import java.io.Writer;
-import java.security.SecureRandom;
 
 import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
-import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
 import org.bouncycastle.util.io.pem.PemGenerationException;
 import org.bouncycastle.util.io.pem.PemObjectGenerator;
 import org.bouncycastle.util.io.pem.PemWriter;
@@ -16,8 +14,6 @@
 public class PEMWriter
     extends PemWriter
 {
-    private String provider;
-
     /**
      * Base constructor.
      * 
@@ -25,26 +21,12 @@
      */
     public PEMWriter(Writer out)
     {
-        this(out, "BC");
-    }
-
-    /**
-     * @deprecated use constructor that just takes out, and writeObject(PEMEncryptor)
-     * @param out
-     * @param provider
-     */
-    public PEMWriter(
-        Writer  out,
-        String  provider)
-    {
         super(out);
-
-        this.provider = provider;
     }
 
     public void writeObject(
-            Object  obj)
-            throws IOException
+        Object  obj)
+        throws IOException
     {
         writeObject(obj, null);
     }
@@ -75,17 +57,4 @@
     {
         super.writeObject(obj);
     }
-
-    /**
-     * @deprecated use writeObject(obj, PEMEncryptor)
-     */
-    public void writeObject(
-        Object       obj,
-        String       algorithm,
-        char[]       password,
-        SecureRandom random)
-        throws IOException
-    {
-        this.writeObject(obj, new JcePEMEncryptorBuilder(algorithm).setSecureRandom(random).setProvider(provider).build(password));
-    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
index 448d885..f822cba 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
@@ -3,20 +3,12 @@
 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.Security;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
-import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
-import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.OutputEncryptor;
 import org.bouncycastle.util.io.pem.PemGenerationException;
 import org.bouncycastle.util.io.pem.PemObject;
@@ -40,56 +32,6 @@
 
     private PrivateKeyInfo key;
     private OutputEncryptor outputEncryptor;
-    private JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder;
-
-    /**
-     * Constructor for an unencrypted private key PEM object.
-     *
-     * @param key private key to be encoded.
-     * @deprecated use JcaPKCS8Generator
-     */
-    public PKCS8Generator(PrivateKey key)
-    {
-        this.key = PrivateKeyInfo.getInstance(key.getEncoded());
-    }
-
-    /**
-     * Constructor for an encrypted private key PEM object.
-     *
-     * @param key       private key to be encoded
-     * @param algorithm encryption algorithm to use
-     * @param provider  name of provider to use
-     * @throws NoSuchProviderException  if provider cannot be found
-     * @throws NoSuchAlgorithmException if algorithm/mode cannot be found
-     *  @deprecated  use JcaPKCS8Generator
-     */
-    public PKCS8Generator(PrivateKey key, ASN1ObjectIdentifier algorithm, String provider)
-        throws NoSuchProviderException, NoSuchAlgorithmException
-    {
-        Provider prov = Security.getProvider(provider);
-
-        if (prov == null)
-        {
-            throw new NoSuchProviderException("cannot find provider: " + provider);
-        }
-
-        init(key, algorithm, prov);
-    }
-
-    /**
-     * Constructor for an encrypted private key PEM object.
-     *
-     * @param key       private key to be encoded
-     * @param algorithm encryption algorithm to use
-     * @param provider  provider to use
-     * @throws NoSuchAlgorithmException if algorithm/mode cannot be found
-     * @deprecated  use JcaPKCS8Generator
-     */
-    public PKCS8Generator(PrivateKey key, ASN1ObjectIdentifier algorithm, Provider provider)
-        throws NoSuchAlgorithmException
-    {
-        init(key, algorithm, provider);
-    }
 
     /**
      * Base constructor.
@@ -100,60 +42,9 @@
         this.outputEncryptor = outputEncryptor;
     }
 
-    private void init(PrivateKey key, ASN1ObjectIdentifier algorithm, Provider provider)
-        throws NoSuchAlgorithmException
-    {
-        this.key = PrivateKeyInfo.getInstance(key.getEncoded());
-        this.encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(algorithm);
-
-        encryptorBuilder.setProvider(provider);
-    }
-
-    /**
-     * @deprecated ignored in the updated case.
-     */
-    public PKCS8Generator setSecureRandom(SecureRandom random)
-    {
-        encryptorBuilder.setRandom(random);
-
-        return this;
-    }
-
-    /**
-     * @deprecated ignored in the updated case.
-     */
-    public PKCS8Generator setPassword(char[] password)
-    {
-        encryptorBuilder.setPasssword(password);
-
-        return this;
-    }
-
-    /**
-     * @deprecated ignored in the updated case.
-     */
-    public PKCS8Generator setIterationCount(int iterationCount)
-    {
-        encryptorBuilder.setIterationCount(iterationCount);
-
-        return this;
-    }
-
     public PemObject generate()
         throws PemGenerationException
     {
-        try
-        {
-            if (encryptorBuilder != null)
-            {
-                outputEncryptor = encryptorBuilder.build();
-            }
-        }
-        catch (OperatorCreationException e)
-        {
-            throw new PemGenerationException("unable to create operator: " + e.getMessage(), e);
-        }
-
         if (outputEncryptor != null)
         {
             return generate(key, outputEncryptor);
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.java
index 4d55aa3..d332ca1 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JcaPEMKeyConverter.java
@@ -2,13 +2,20 @@
 
 import java.security.KeyFactory;
 import java.security.KeyPair;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
 import java.security.Provider;
 import java.security.PublicKey;
 import java.security.spec.PKCS8EncodedKeySpec;
 import java.security.spec.X509EncodedKeySpec;
+import java.util.HashMap;
+import java.util.Map;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.jcajce.DefaultJcaJceHelper;
@@ -22,6 +29,15 @@
 {
     private JcaJceHelper helper = new DefaultJcaJceHelper();
 
+    private static final Map algorithms = new HashMap();
+
+    static
+    {
+        algorithms.put(X9ObjectIdentifiers.id_ecPublicKey, "ECDSA");
+        algorithms.put(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+        algorithms.put(X9ObjectIdentifiers.id_dsa, "DSA");
+    }
+
     public JcaPEMKeyConverter setProvider(Provider provider)
     {
         this.helper = new ProviderJcaJceHelper(provider);
@@ -41,14 +57,7 @@
     {
         try
         {
-            String algorithm =  keyPair.getPrivateKeyInfo().getPrivateKeyAlgorithm().getAlgorithm().getId();
-
-            if (X9ObjectIdentifiers.id_ecPublicKey.getId().equals(algorithm))
-            {
-                algorithm = "ECDSA";
-            }
-
-            KeyFactory keyFactory = helper.createKeyFactory(algorithm);
+            KeyFactory keyFactory = getKeyFactory(keyPair.getPrivateKeyInfo().getPrivateKeyAlgorithm());
 
             return new KeyPair(keyFactory.generatePublic(new X509EncodedKeySpec(keyPair.getPublicKeyInfo().getEncoded())),
                                 keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyPair.getPrivateKeyInfo().getEncoded())));
@@ -64,14 +73,7 @@
     {
         try
         {
-            String algorithm =  publicKeyInfo.getAlgorithm().getAlgorithm().getId();
-
-            if (X9ObjectIdentifiers.id_ecPublicKey.getId().equals(algorithm))
-            {
-                algorithm = "ECDSA";
-            }
-
-            KeyFactory keyFactory = helper.createKeyFactory(algorithm);
+            KeyFactory keyFactory = getKeyFactory(publicKeyInfo.getAlgorithm());
 
             return keyFactory.generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded()));
         }
@@ -86,14 +88,7 @@
     {
         try
         {
-            String algorithm =  privateKeyInfo.getPrivateKeyAlgorithm().getAlgorithm().getId();
-
-            if (X9ObjectIdentifiers.id_ecPublicKey.getId().equals(algorithm))
-            {
-                algorithm = "ECDSA";
-            }
-
-            KeyFactory keyFactory = helper.createKeyFactory(algorithm);
+            KeyFactory keyFactory = getKeyFactory(privateKeyInfo.getPrivateKeyAlgorithm());
 
             return keyFactory.generatePrivate(new PKCS8EncodedKeySpec(privateKeyInfo.getEncoded()));
         }
@@ -102,4 +97,19 @@
             throw new PEMException("unable to convert key pair: " + e.getMessage(), e);
         }
     }
+
+    private KeyFactory getKeyFactory(AlgorithmIdentifier algId)
+        throws NoSuchAlgorithmException, NoSuchProviderException
+    {
+        ASN1ObjectIdentifier algorithm =  algId.getAlgorithm();
+
+        String algName = (String)algorithms.get(algorithm);
+
+        if (algName == null)
+        {
+            algName = algorithm.getId();
+        }
+
+        return helper.createKeyFactory(algName);
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/package.html b/bcpkix/src/main/java/org/bouncycastle/openssl/package.html
deleted file mode 100644
index 7e60a79..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for dealing with OpenSSL PEM files.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
index eb1d4da..faae18e 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
@@ -15,14 +15,18 @@
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.PEMWriter;
 import org.bouncycastle.openssl.PKCS8Generator;
-import org.bouncycastle.openssl.PasswordFinder;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
 import org.bouncycastle.openssl.jcajce.JcaPKCS8Generator;
+import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8DecryptorProviderBuilder;
 import org.bouncycastle.openssl.jcajce.JceOpenSSLPKCS8EncryptorBuilder;
 import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.pkcs.PKCSException;
 import org.bouncycastle.util.test.SimpleTestResult;
 
 public class
@@ -30,12 +34,14 @@
     extends TestCase
 {
     public void testOpenSSL()
-    {   
-        Security.addProvider(new BouncyCastleProvider());
+    {
+        if (Security.getProvider("BC") == null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
         
         org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[]
         {
-            new ReaderTest(),
             new WriterTest(),
             new ParserTest()
         };
@@ -54,51 +60,27 @@
     public void testPKCS8Encrypted()
         throws Exception
     {
+        if (Security.getProvider("BC") == null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+
         KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
 
         kpGen.initialize(1024);
 
         PrivateKey key = kpGen.generateKeyPair().getPrivate();
 
-        encryptedTest(key, PKCS8Generator.AES_256_CBC);
-        encryptedTest(key, PKCS8Generator.DES3_CBC);
-        encryptedTest(key, PKCS8Generator.PBE_SHA1_3DES);
         encryptedTestNew(key, PKCS8Generator.AES_256_CBC);
         encryptedTestNew(key, PKCS8Generator.DES3_CBC);
         encryptedTestNew(key, PKCS8Generator.PBE_SHA1_3DES);
     }
 
-    private void encryptedTest(PrivateKey key, ASN1ObjectIdentifier algorithm)
-        throws NoSuchProviderException, NoSuchAlgorithmException, IOException
-    {
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut), "BC");
-        PKCS8Generator pkcs8 = new PKCS8Generator(key, algorithm, "BC");
-
-        pkcs8.setPassword("hello".toCharArray());
-        
-        pWrt.writeObject(pkcs8);
-
-        pWrt.close();
-
-        PEMReader pRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())), new PasswordFinder()
-        {
-            public char[] getPassword()
-            {
-                return "hello".toCharArray();
-            }
-        });
-
-        PrivateKey rdKey = (PrivateKey)pRd.readObject();
-
-        assertEquals(key, rdKey);
-    }
-
     private void encryptedTestNew(PrivateKey key, ASN1ObjectIdentifier algorithm)
-        throws NoSuchProviderException, NoSuchAlgorithmException, IOException, OperatorCreationException
+        throws NoSuchProviderException, NoSuchAlgorithmException, IOException, OperatorCreationException, PKCSException
     {
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut), "BC");
+        PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut));
 
         JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(algorithm);
 
@@ -111,44 +93,12 @@
 
         pWrt.close();
 
-        PEMReader pRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())), new PasswordFinder()
-        {
-            public char[] getPassword()
-            {
-                return "hello".toCharArray();
-            }
-        });
+        PEMParser pRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
 
-        PrivateKey rdKey = (PrivateKey)pRd.readObject();
+        PKCS8EncryptedPrivateKeyInfo pInfo = (PKCS8EncryptedPrivateKeyInfo)pRd.readObject();
 
-        assertEquals(key, rdKey);
-    }
+        PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(pInfo.decryptPrivateKeyInfo(new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray())));
 
-    public void testPKCS8Plain()
-        throws Exception
-    {
-        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
-
-        kpGen.initialize(1024);
-
-        PrivateKey key = kpGen.generateKeyPair().getPrivate();
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        PEMWriter pWrt = new PEMWriter(new OutputStreamWriter(bOut));
-        PKCS8Generator pkcs8 = new PKCS8Generator(key);
-
-        pWrt.writeObject(pkcs8);
-
-        pWrt.close();
-
-        PEMReader pRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())), new PasswordFinder()
-        {
-            public char[] getPassword()
-            {
-                return "hello".toCharArray();
-            }
-        });
-
-        PrivateKey rdKey = (PrivateKey)pRd.readObject();
 
         assertEquals(key, rdKey);
     }
@@ -156,6 +106,11 @@
     public void testPKCS8PlainNew()
         throws Exception
     {
+        if (Security.getProvider("BC") == null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+
         KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", "BC");
 
         kpGen.initialize(1024);
@@ -169,15 +124,11 @@
 
         pWrt.close();
 
-        PEMReader pRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())), new PasswordFinder()
-        {
-            public char[] getPassword()
-            {
-                return "hello".toCharArray();
-            }
-        });
+        PEMParser pRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
 
-        PrivateKey rdKey = (PrivateKey)pRd.readObject();
+        PrivateKeyInfo kp = (PrivateKeyInfo)pRd.readObject();
+
+        PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(kp);
 
         assertEquals(key, rdKey);
     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/test/ReaderTest.java b/bcpkix/src/main/java/org/bouncycastle/openssl/test/ReaderTest.java
deleted file mode 100644
index 23aee08..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/ReaderTest.java
+++ /dev/null
@@ -1,417 +0,0 @@
-package org.bouncycastle.openssl.test;
-
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.OutputStreamWriter;
-import java.io.Reader;
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.security.Signature;
-import java.security.cert.X509Certificate;
-import java.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.RSAPrivateCrtKey;
-import java.security.interfaces.RSAPrivateKey;
-
-import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
-import org.bouncycastle.asn1.cms.ContentInfo;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
-import org.bouncycastle.openssl.PEMReader;
-import org.bouncycastle.openssl.PEMWriter;
-import org.bouncycastle.openssl.PasswordFinder;
-import org.bouncycastle.util.test.SimpleTest;
-
-/**
- * basic class for reading test.pem - the password is "secret"
- */
-public class ReaderTest
-    extends SimpleTest
-{
-    private static class Password
-        implements PasswordFinder
-    {
-        char[]  password;
-
-        Password(
-            char[] word)
-        {
-            this.password = word;
-        }
-
-        public char[] getPassword()
-        {
-            return password;
-        }
-    }
-
-    public String getName()
-    {
-        return "PEMReaderTest";
-    }
-
-    private PEMReader openPEMResource(
-        String          fileName,
-        PasswordFinder  pGet)
-    {
-        InputStream res = this.getClass().getResourceAsStream(fileName);
-        Reader fRd = new BufferedReader(new InputStreamReader(res));
-        return new PEMReader(fRd, pGet);
-    }
-
-    public void performTest()
-        throws Exception
-    {
-        PasswordFinder  pGet = new Password("secret".toCharArray());
-        PEMReader       pemRd = openPEMResource("test.pem", pGet);
-        Object          o;
-        KeyPair         pair;
-
-        while ((o = pemRd.readObject()) != null)
-        {
-            if (o instanceof KeyPair)
-            {
-                //pair = (KeyPair)o;
-
-                //System.out.println(pair.getPublic());
-                //System.out.println(pair.getPrivate());
-            }
-            else
-            {
-                //System.out.println(o.toString());
-            }
-        }
-
-        // test bogus lines before begin are ignored.
-        pemRd = openPEMResource("extratest.pem", pGet);
-
-        while ((o = pemRd.readObject()) != null)
-        {
-            if (!(o instanceof X509Certificate))
-            {
-                fail("wrong object found");
-            }
-        }
-
-        //
-        // pkcs 7 data
-        //
-        pemRd = openPEMResource("pkcs7.pem", null);
-        ContentInfo d = (ContentInfo)pemRd.readObject();
-
-        if (!d.getContentType().equals(CMSObjectIdentifiers.envelopedData))
-        {
-            fail("failed envelopedData check");
-        }
-
-        //
-        // ECKey
-        //
-        pemRd = openPEMResource("eckey.pem", null);
-        ECNamedCurveParameterSpec spec = (ECNamedCurveParameterSpec)pemRd.readObject();
-
-        pair = (KeyPair)pemRd.readObject();
-        Signature sgr = Signature.getInstance("ECDSA", "BC");
-
-        sgr.initSign(pair.getPrivate());
-
-        byte[] message = new byte[] { (byte)'a', (byte)'b', (byte)'c' };
-
-        sgr.update(message);
-
-        byte[]  sigBytes = sgr.sign();
-
-        sgr.initVerify(pair.getPublic());
-
-        sgr.update(message);
-
-        if (!sgr.verify(sigBytes))
-        {
-            fail("EC verification failed");
-        }
-
-        if (!pair.getPublic().getAlgorithm().equals("ECDSA"))
-        {
-            fail("wrong algorithm name on public got: " + pair.getPublic().getAlgorithm());
-        }
-
-        if (!pair.getPrivate().getAlgorithm().equals("ECDSA"))
-        {
-            fail("wrong algorithm name on private");
-        }
-
-        //
-        // writer/parser test
-        //
-        KeyPairGenerator      kpGen = KeyPairGenerator.getInstance("RSA", "BC");
-
-        pair = kpGen.generateKeyPair();
-
-        keyPairTest("RSA", pair);
-
-        kpGen = KeyPairGenerator.getInstance("DSA", "BC");
-        kpGen.initialize(512, new SecureRandom());
-        pair = kpGen.generateKeyPair();
-
-        keyPairTest("DSA", pair);
-
-        //
-        // PKCS7
-        //
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        PEMWriter             pWrt = new PEMWriter(new OutputStreamWriter(bOut));
-
-        pWrt.writeObject(d);
-
-        pWrt.close();
-
-        pemRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
-        d = (ContentInfo)pemRd.readObject();
-
-        if (!d.getContentType().equals(CMSObjectIdentifiers.envelopedData))
-        {
-            fail("failed envelopedData recode check");
-        }
-
-
-        // OpenSSL test cases (as embedded resources)
-        doOpenSslDsaTest("unencrypted");
-        doOpenSslRsaTest("unencrypted");
-
-        doOpenSslTests("aes128");
-        doOpenSslTests("aes192");
-        doOpenSslTests("aes256");
-        doOpenSslTests("blowfish");
-        doOpenSslTests("des1");
-        doOpenSslTests("des2");
-        doOpenSslTests("des3");
-        doOpenSslTests("rc2_128");
-
-        doOpenSslDsaTest("rc2_40_cbc");
-        doOpenSslRsaTest("rc2_40_cbc");
-        doOpenSslDsaTest("rc2_64_cbc");
-        doOpenSslRsaTest("rc2_64_cbc");
-
-        doDudPasswordTest("7fd98", 0, "corrupted stream - out of bounds length found");
-        doDudPasswordTest("ef677", 1, "corrupted stream - out of bounds length found");
-        doDudPasswordTest("800ce", 2, "unknown tag 26 encountered");
-        doDudPasswordTest("b6cd8", 3, "DEF length 81 object truncated by 56");
-        doDudPasswordTest("28ce09", 4, "DEF length 110 object truncated by 28");
-        doDudPasswordTest("2ac3b9", 5, "DER length more than 4 bytes: 11");
-        doDudPasswordTest("2cba96", 6, "DEF length 100 object truncated by 35");
-        doDudPasswordTest("2e3354", 7, "DEF length 42 object truncated by 9");
-        doDudPasswordTest("2f4142", 8, "DER length more than 4 bytes: 14");
-        doDudPasswordTest("2fe9bb", 9, "DER length more than 4 bytes: 65");
-        doDudPasswordTest("3ee7a8", 10, "DER length more than 4 bytes: 57");
-        doDudPasswordTest("41af75", 11, "unknown tag 16 encountered");
-        doDudPasswordTest("1704a5", 12, "corrupted stream detected");
-        doDudPasswordTest("1c5822", 13, "unknown object in getInstance: org.bouncycastle.asn1.DERUTF8String");
-        doDudPasswordTest("5a3d16", 14, "corrupted stream detected");
-        doDudPasswordTest("8d0c97", 15, "corrupted stream detected");
-        doDudPasswordTest("bc0daf", 16, "corrupted stream detected");
-        doDudPasswordTest("aaf9c4d",17, "corrupted stream - out of bounds length found");
-
-        doNoPasswordTest();
-
-        // encrypted private key test
-        pGet = new Password("password".toCharArray());
-        pemRd = openPEMResource("enckey.pem", pGet);
-
-        RSAPrivateCrtKey privKey = (RSAPrivateCrtKey)pemRd.readObject();
-
-        if (!privKey.getPublicExponent().equals(new BigInteger("10001", 16)))
-        {
-            fail("decryption of private key data check failed");
-        }
-
-        // general PKCS8 test
-        pGet = new Password("password".toCharArray());
-        pemRd = openPEMResource("pkcs8test.pem", pGet);
-
-        while ((privKey = (RSAPrivateCrtKey)pemRd.readObject()) != null)
-        {
-            if (!privKey.getPublicExponent().equals(new BigInteger("10001", 16)))
-            {
-                fail("decryption of private key data check failed");
-            }
-        }
-    }
-
-    private void keyPairTest(
-        String   name,
-        KeyPair pair) 
-        throws IOException
-    {
-        PEMReader pemRd;
-        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-        PEMWriter             pWrt = new PEMWriter(new OutputStreamWriter(bOut));
-        
-        pWrt.writeObject(pair.getPublic());
-        
-        pWrt.close();
-
-        pemRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
-        
-        PublicKey k = (PublicKey)pemRd.readObject();
-        if (!k.equals(pair.getPublic()))
-        {
-            fail("Failed public key read: " + name);
-        }
-        
-        bOut = new ByteArrayOutputStream();
-        pWrt = new PEMWriter(new OutputStreamWriter(bOut));
-        
-        pWrt.writeObject(pair.getPrivate());
-        
-        pWrt.close();
-        
-        pemRd = new PEMReader(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
-        
-        KeyPair kPair = (KeyPair)pemRd.readObject();
-        if (!kPair.getPrivate().equals(pair.getPrivate()))
-        {
-            fail("Failed private key read: " + name);
-        }
-        
-        if (!kPair.getPublic().equals(pair.getPublic()))
-        {
-            fail("Failed private key public read: " + name);
-        }
-    }
-
-    private void doOpenSslTests(
-        String baseName)
-        throws IOException
-    {
-        doOpenSslDsaModesTest(baseName);
-        doOpenSslRsaModesTest(baseName);
-    }
-
-    private void doOpenSslDsaModesTest(
-        String baseName)
-        throws IOException
-    {
-        doOpenSslDsaTest(baseName + "_cbc");
-        doOpenSslDsaTest(baseName + "_cfb");
-        doOpenSslDsaTest(baseName + "_ecb");
-        doOpenSslDsaTest(baseName + "_ofb");
-    }
-
-    private void doOpenSslRsaModesTest(
-        String baseName)
-        throws IOException
-    {
-        doOpenSslRsaTest(baseName + "_cbc");
-        doOpenSslRsaTest(baseName + "_cfb");
-        doOpenSslRsaTest(baseName + "_ecb");
-        doOpenSslRsaTest(baseName + "_ofb");
-    }
-
-    private void doOpenSslDsaTest(
-        String name)
-        throws IOException
-    {
-        String fileName = "dsa/openssl_dsa_" + name + ".pem";
-
-        doOpenSslTestFile(fileName, DSAPrivateKey.class);
-    }
-
-    private void doOpenSslRsaTest(
-        String name)
-        throws IOException
-    {
-        String fileName = "rsa/openssl_rsa_" + name + ".pem";
-
-        doOpenSslTestFile(fileName, RSAPrivateKey.class);
-    }
-
-    private void doOpenSslTestFile(
-        String  fileName,
-        Class   expectedPrivKeyClass)
-        throws IOException
-    {
-        PEMReader pr = openPEMResource("data/" + fileName, new Password("changeit".toCharArray()));
-        Object o = pr.readObject();
-
-        if (o == null || !(o instanceof KeyPair))
-        {
-            fail("Didn't find OpenSSL key");
-        }
-
-        KeyPair kp = (KeyPair) o;
-        PrivateKey privKey = kp.getPrivate();
-
-        if (!expectedPrivKeyClass.isInstance(privKey))
-        {
-            fail("Returned key not of correct type");
-        }
-    }
-
-    private void doDudPasswordTest(String password, int index, String message)
-    {
-        // illegal state exception check - in this case the wrong password will
-        // cause an underlying class cast exception.
-        try
-        {
-            PasswordFinder pGet = new Password(password.toCharArray());
-
-            PEMReader pemRd = openPEMResource("test.pem", pGet);
-            Object o;
-
-            while ((o = pemRd.readObject()) != null)
-            {
-            }
-
-            fail("issue not detected: " + index);
-        }
-        catch (IOException e)
-        {
-            if (e.getCause() != null && !e.getCause().getMessage().equals(message))
-            {
-               e.printStackTrace();
-               fail("issue " + index + " exception thrown, but wrong message");
-            }
-            else if (e.getCause() == null && !e.getMessage().equals(message))
-            {
-                               e.printStackTrace();
-               fail("issue " + index + " exception thrown, but wrong message");
-            }
-        }
-    }
-
-    private void doNoPasswordTest()
-        throws IOException
-    {
-        PasswordFinder pGet = new Password("".toCharArray());
-
-        PEMReader pemRd = openPEMResource("smimenopw.pem", pGet);
-        Object o;
-        PrivateKey key = null;
-
-        while ((o = pemRd.readObject()) != null)
-        {
-             key = (PrivateKey)o;
-        }
-
-        if (key == null)
-        {
-            fail("private key not detected");
-        }
-    }
-
-    public static void main(
-        String[]    args)
-    {
-        Security.addProvider(new BouncyCastleProvider());
-
-        runTest(new ReaderTest());
-    }
-}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/test/WriterTest.java b/bcpkix/src/main/java/org/bouncycastle/openssl/test/WriterTest.java
index cb911eb..e41efd6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/WriterTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/test/WriterTest.java
@@ -18,13 +18,18 @@
 import java.util.List;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.openssl.PEMReader;
+import org.bouncycastle.openssl.PEMEncryptedKeyPair;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.PEMWriter;
 import org.bouncycastle.openssl.PasswordFinder;
+import org.bouncycastle.openssl.jcajce.JcaMiscPEMGenerator;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.openssl.jcajce.JcePEMDecryptorProviderBuilder;
+import org.bouncycastle.openssl.jcajce.JcePEMEncryptorBuilder;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.io.pem.PemHeader;
 import org.bouncycastle.util.io.pem.PemObject;
-import org.bouncycastle.util.io.pem.PemReader;
 import org.bouncycastle.util.test.SimpleTest;
 
 public class WriterTest
@@ -154,23 +159,23 @@
         throws IOException
     {
         StringWriter sw = new StringWriter();
-        PEMWriter pw = new PEMWriter(sw, provider);
+        PEMWriter pw = new PEMWriter(sw);
 
         pw.writeObject(akp);
         pw.close();
 
         String data = sw.toString();
 
-        PEMReader pr = new PEMReader(new StringReader(data));
+        PEMParser pr = new PEMParser(new StringReader(data));
 
         Object o = pr.readObject();
 
-        if (o == null || !(o instanceof KeyPair))
+        if (o == null || !(o instanceof PEMKeyPair))
         {
             fail("Didn't find OpenSSL key");
         }
 
-        KeyPair kp = (KeyPair) o;
+        KeyPair kp = new JcaPEMKeyConverter().setProvider("BC").getKeyPair((PEMKeyPair)o);
         PrivateKey privKey = kp.getPrivate();
 
         if (!akp.equals(privKey))
@@ -186,14 +191,14 @@
         throws IOException
     {
         StringWriter sw = new StringWriter();
-        PEMWriter pw = new PEMWriter(sw, provider);
+        PEMWriter pw = new PEMWriter(sw);
 
-        pw.writeObject(akp, algorithm, testPassword, random);
+        pw.writeObject(new JcaMiscPEMGenerator(akp, new JcePEMEncryptorBuilder(algorithm).setSecureRandom(random).build(testPassword)));
         pw.close();
 
         String data = sw.toString();
 
-        PemReader pRaw = new PemReader(new StringReader(data));
+        PEMParser pRaw = new PEMParser(new StringReader(data));
         PemObject pemObject = pRaw.readPemObject();
 
         List headers = pemObject.getHeaders();
@@ -215,16 +220,16 @@
             }
         }
 
-        PEMReader pr = new PEMReader(new StringReader(data), new Password(testPassword), provider);
+        PEMParser pr = new PEMParser(new StringReader(data));
 
         Object o = pr.readObject();
 
-        if (o == null || !(o instanceof KeyPair))
+        if (o == null || !(o instanceof PEMEncryptedKeyPair))
         {
             fail("Didn't find OpenSSL key");
         }
 
-        KeyPair kp = (KeyPair) o;
+        KeyPair kp = new JcaPEMKeyConverter().setProvider("BC").getKeyPair(((PEMEncryptedKeyPair)o).decryptKeyPair(new JcePEMDecryptorProviderBuilder().setProvider("BC").build(testPassword)));
         PrivateKey privKey = kp.getPrivate();
 
         if (!akp.equals(privKey))
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/BufferingContentSigner.java b/bcpkix/src/main/java/org/bouncycastle/operator/BufferingContentSigner.java
new file mode 100644
index 0000000..d174367
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/BufferingContentSigner.java
@@ -0,0 +1,70 @@
+package org.bouncycastle.operator;
+
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.util.io.BufferingOutputStream;
+
+/**
+ * A class that explicitly buffers the data to be signed, sending it in one
+ * block when ready for signing.
+ */
+public class BufferingContentSigner
+    implements ContentSigner
+{
+    private final ContentSigner contentSigner;
+    private final OutputStream  output;
+
+    /**
+     * Base constructor.
+     *
+     * @param contentSigner the content signer to be wrapped.
+     */
+    public BufferingContentSigner(ContentSigner contentSigner)
+    {
+        this.contentSigner = contentSigner;
+        this.output = new BufferingOutputStream(contentSigner.getOutputStream());
+    }
+
+    /**
+     * Base constructor.
+     *
+     * @param contentSigner the content signer to be wrapped.
+     * @param bufferSize the size of the internal buffer to use.
+     */
+    public BufferingContentSigner(ContentSigner contentSigner, int bufferSize)
+    {
+        this.contentSigner = contentSigner;
+        this.output = new BufferingOutputStream(contentSigner.getOutputStream(), bufferSize);
+    }
+
+    /**
+     * Return the algorithm identifier supported by this signer.
+     *
+     * @return algorithm identifier for the signature generated.
+     */
+    public AlgorithmIdentifier getAlgorithmIdentifier()
+    {
+        return contentSigner.getAlgorithmIdentifier();
+    }
+
+    /**
+     * Return the buffering stream.
+     *
+     * @return the output stream used to accumulate the data.
+     */
+    public OutputStream getOutputStream()
+    {
+        return output;
+    }
+
+    /**
+     * Generate signature from internally buffered data.
+     *
+     * @return the signature calculated from the bytes written to the buffering stream.
+     */
+    public byte[] getSignature()
+    {
+        return contentSigner.getSignature();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
similarity index 68%
rename from bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java
rename to bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
index 234c38b..a1c6ba1 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeyProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
@@ -5,16 +5,17 @@
 import java.util.Map;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.util.Integers;
 
-public class DefaultSecretKeyProvider
+public class DefaultSecretKeySizeProvider
     implements SecretKeySizeProvider
 {
-    public static final SecretKeySizeProvider INSTANCE = new DefaultSecretKeyProvider();
+    public static final SecretKeySizeProvider INSTANCE = new DefaultSecretKeySizeProvider();
 
     private static final Map KEY_SIZES;
 
@@ -24,7 +25,7 @@
 
         keySizes.put(new ASN1ObjectIdentifier("1.2.840.113533.7.66.10"), Integers.valueOf(128));
 
-        keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192));
+        keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC, Integers.valueOf(192));
 
         keySizes.put(NISTObjectIdentifiers.id_aes128_CBC, Integers.valueOf(128));
         keySizes.put(NISTObjectIdentifiers.id_aes192_CBC, Integers.valueOf(192));
@@ -34,13 +35,29 @@
         keySizes.put(NTTObjectIdentifiers.id_camellia192_cbc, Integers.valueOf(192));
         keySizes.put(NTTObjectIdentifiers.id_camellia256_cbc, Integers.valueOf(256));
 
+        keySizes.put(CryptoProObjectIdentifiers.gostR28147_gcfb, Integers.valueOf(256));
+
         KEY_SIZES = Collections.unmodifiableMap(keySizes);
     }
 
     public int getKeySize(AlgorithmIdentifier algorithmIdentifier)
     {
-        // TODO: not all ciphers/oid relationships are this simple.
-        Integer keySize = (Integer)KEY_SIZES.get(algorithmIdentifier.getAlgorithm());
+        int keySize = getKeySize(algorithmIdentifier.getAlgorithm());
+
+        // just need the OID
+        if (keySize > 0)
+        {
+            return keySize;
+        }
+
+        // TODO: support OID/Parameter key sizes (e.g. RC2).
+
+        return -1;
+    }
+
+    public int getKeySize(ASN1ObjectIdentifier algorithm)
+    {
+        Integer keySize = (Integer)KEY_SIZES.get(algorithm);
 
         if (keySize != null)
         {
@@ -49,6 +66,4 @@
 
         return -1;
     }
-
-
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/SecretKeySizeProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/SecretKeySizeProvider.java
index 15d7a67..5f92ef0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/SecretKeySizeProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/SecretKeySizeProvider.java
@@ -1,8 +1,17 @@
 package org.bouncycastle.operator;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 public interface SecretKeySizeProvider
 {
     int getKeySize(AlgorithmIdentifier algorithmIdentifier);
+
+    /**
+     * Return the key size implied by the OID, if one exists.
+     *
+     * @param algorithm the OID of the algorithm of interest.
+     * @return -1 if there is no fixed key size associated with the OID, or more information is required.
+     */
+    int getKeySize(ASN1ObjectIdentifier algorithm);
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaAlgorithmParametersConverter.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaAlgorithmParametersConverter.java
new file mode 100644
index 0000000..d4e2162
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaAlgorithmParametersConverter.java
@@ -0,0 +1,73 @@
+package org.bouncycastle.operator.jcajce;
+
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.MGF1ParameterSpec;
+
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+
+public class JcaAlgorithmParametersConverter
+{
+    public JcaAlgorithmParametersConverter()
+    {
+    }
+
+    public AlgorithmIdentifier getAlgorithmIdentifier(ASN1ObjectIdentifier algId, AlgorithmParameters parameters)
+        throws InvalidAlgorithmParameterException
+    {
+        try
+        {
+            ASN1Encodable params = ASN1Primitive.fromByteArray(parameters.getEncoded());
+
+            return new AlgorithmIdentifier(algId, params);
+        }
+        catch (IOException e)
+        {
+            throw new InvalidAlgorithmParameterException("unable to encode parameters object: " + e.getMessage());
+        }
+    }
+
+    public AlgorithmIdentifier getAlgorithmIdentifier(ASN1ObjectIdentifier algorithm, AlgorithmParameterSpec algorithmSpec)
+        throws InvalidAlgorithmParameterException
+    {
+        if (algorithmSpec instanceof OAEPParameterSpec)
+        {
+            if (algorithmSpec.equals(OAEPParameterSpec.DEFAULT))
+            {
+                return new AlgorithmIdentifier(algorithm,
+                    new RSAESOAEPparams(RSAESOAEPparams.DEFAULT_HASH_ALGORITHM, RSAESOAEPparams.DEFAULT_MASK_GEN_FUNCTION, RSAESOAEPparams.DEFAULT_P_SOURCE_ALGORITHM));
+            }
+            else
+            {
+                OAEPParameterSpec oaepSpec = (OAEPParameterSpec)algorithmSpec;
+                PSource pSource = oaepSpec.getPSource();
+
+                if (!oaepSpec.getMGFAlgorithm().equals(OAEPParameterSpec.DEFAULT.getMGFAlgorithm()))
+                {
+                    throw new InvalidAlgorithmParameterException("only " + OAEPParameterSpec.DEFAULT.getMGFAlgorithm() + " mask generator supported.");
+                }
+
+                AlgorithmIdentifier hashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find(oaepSpec.getDigestAlgorithm());
+                AlgorithmIdentifier mgf1HashAlgorithm = new DefaultDigestAlgorithmIdentifierFinder().find((((MGF1ParameterSpec)oaepSpec.getMGFParameters()).getDigestAlgorithm()));
+                return new AlgorithmIdentifier(algorithm,
+                                    new RSAESOAEPparams(hashAlgorithm, new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, mgf1HashAlgorithm),
+                                                        new AlgorithmIdentifier(PKCSObjectIdentifiers.id_pSpecified, new DEROctetString(((PSource.PSpecified)pSource).getValue()))));
+            }
+        }
+
+        throw new InvalidAlgorithmParameterException("unknown parameter spec passed.");
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
index 56c3771..87a6699 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
@@ -12,6 +12,7 @@
 import java.security.cert.X509Certificate;
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.jcajce.DefaultJcaJceHelper;
@@ -144,6 +145,12 @@
         };
     }
 
+    public ContentVerifierProvider build(SubjectPublicKeyInfo publicKey)
+        throws OperatorCreationException
+    {
+        return this.build(helper.convertPublicKey(publicKey));
+    }
+
     private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey)
         throws OperatorCreationException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
index 9413f96..9140ef2 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
@@ -1,5 +1,6 @@
 package org.bouncycastle.operator.jcajce;
 
+import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
 import java.security.Key;
@@ -80,10 +81,18 @@
             Key sKey = null;
 
             Cipher keyCipher = helper.createAsymmetricWrapper(this.getAlgorithmIdentifier().getAlgorithm(), extraMappings);
+            AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier());
 
             try
             {
-                keyCipher.init(Cipher.UNWRAP_MODE, privKey);
+                if (algParams != null)
+                {
+                    keyCipher.init(Cipher.UNWRAP_MODE, privKey, algParams);
+                }
+                else
+                {
+                    keyCipher.init(Cipher.UNWRAP_MODE, privKey);
+                }
                 sKey = keyCipher.unwrap(encryptedKey, helper.getKeyAlgorithmName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY);
             }
             catch (GeneralSecurityException e)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
index 4a2ffae..d19dbcf 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
@@ -1,6 +1,8 @@
 package org.bouncycastle.operator.jcajce;
 
+import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
+import java.security.InvalidKeyException;
 import java.security.Provider;
 import java.security.ProviderException;
 import java.security.PublicKey;
@@ -12,6 +14,7 @@
 import javax.crypto.Cipher;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.jcajce.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.NamedJcaJceHelper;
@@ -40,6 +43,19 @@
         this(certificate.getPublicKey());
     }
 
+    /**
+     * Create a wrapper, overriding the algorithm type that is stored in the public key.
+     *
+     * @param algorithmIdentifier identifier for encryption algorithm to be used.
+     * @param publicKey the public key to be used.
+     */
+    public JceAsymmetricKeyWrapper(AlgorithmIdentifier algorithmIdentifier, PublicKey publicKey)
+    {
+        super(algorithmIdentifier);
+
+        this.publicKey = publicKey;
+    }
+
     public JceAsymmetricKeyWrapper setProvider(Provider provider)
     {
         this.helper = new OperatorHelper(new ProviderJcaJceHelper(provider));
@@ -86,13 +102,25 @@
         throws OperatorException
     {
         Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), extraMappings);
+        AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier());
+
         byte[] encryptedKeyBytes = null;
 
         try
         {
-            keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, random);
+            if (algParams != null)
+            {
+                keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, algParams, random);
+            }
+            else
+            {
+                keyEncryptionCipher.init(Cipher.WRAP_MODE, publicKey, random);
+            }
             encryptedKeyBytes = keyEncryptionCipher.wrap(OperatorUtils.getJceKey(encryptionKey));
         }
+        catch (InvalidKeyException e)
+        {
+        }
         catch (GeneralSecurityException e)
         {
         }
@@ -114,6 +142,10 @@
                 keyEncryptionCipher.init(Cipher.ENCRYPT_MODE, publicKey, random);
                 encryptedKeyBytes = keyEncryptionCipher.doFinal(OperatorUtils.getJceKey(encryptionKey).getEncoded());
             }
+            catch (InvalidKeyException e)
+            {
+                throw new OperatorException("unable to encrypt contents key", e);
+            }
             catch (GeneralSecurityException e)
             {
                 throw new OperatorException("unable to encrypt contents key", e);
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
index bdffa53..2e0bcc1 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -4,14 +4,18 @@
 import java.io.IOException;
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
+import java.security.KeyFactory;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
+import java.security.PublicKey;
 import java.security.Signature;
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
 import java.security.spec.PSSParameterSpec;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -29,9 +33,11 @@
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.jcajce.JcaJceHelper;
+import org.bouncycastle.jcajce.JcaJceUtils;
 import org.bouncycastle.operator.OperatorCreationException;
 
 class OperatorHelper
@@ -181,6 +187,41 @@
         }
     }
 
+    AlgorithmParameters createAlgorithmParameters(AlgorithmIdentifier cipherAlgId)
+        throws OperatorCreationException
+    {
+        AlgorithmParameters parameters;
+
+        if (cipherAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.rsaEncryption))
+        {
+            return null;
+        }
+
+        try
+        {
+            parameters = helper.createAlgorithmParameters(cipherAlgId.getAlgorithm().getId());
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            return null;   // There's a good chance there aren't any!
+        }
+        catch (NoSuchProviderException e)
+        {
+            throw new OperatorCreationException("cannot create algorithm parameters: " + e.getMessage(), e);
+        }
+
+        try
+        {
+            parameters.init(cipherAlgId.getParameters().toASN1Primitive().getEncoded());
+        }
+        catch (IOException e)
+        {
+            throw new OperatorCreationException("cannot initialise algorithm parameters: " + e.getMessage(), e);
+        }
+
+        return parameters;
+    }
+
     MessageDigest createDigest(AlgorithmIdentifier digAlgId)
         throws GeneralSecurityException
     {
@@ -258,7 +299,7 @@
             {
                 AlgorithmParameters params = helper.createAlgorithmParameters(algName);
 
-                params.init(algorithm.getParameters().toASN1Primitive().getEncoded(), "ASN.1");
+                JcaJceUtils.loadParameters(params, algorithm.getParameters());
 
                 PSSParameterSpec spec = (PSSParameterSpec)params.getParameterSpec(PSSParameterSpec.class);
                 sig.setParameter(spec);
@@ -367,6 +408,33 @@
         }
     }
 
+    public PublicKey convertPublicKey(SubjectPublicKeyInfo publicKeyInfo)
+        throws OperatorCreationException
+    {
+        try
+        {
+            KeyFactory keyFact = helper.createKeyFactory(publicKeyInfo.getAlgorithm().getAlgorithm().getId());
+
+            return keyFact.generatePublic(new X509EncodedKeySpec(publicKeyInfo.getEncoded()));
+        }
+        catch (IOException e)
+        {
+            throw new OperatorCreationException("cannot get encoded form of key: " + e.getMessage(), e);
+        }
+        catch (NoSuchAlgorithmException e)
+        {
+            throw new OperatorCreationException("cannot create key factory: " + e.getMessage(), e);
+        }
+        catch (NoSuchProviderException e)
+        {
+            throw new OperatorCreationException("cannot find factory provider: " + e.getMessage(), e);
+        }
+        catch (InvalidKeySpecException e)
+        {
+            throw new OperatorCreationException("cannot create key factory: " + e.getMessage(), e);
+        }
+    }
+
     // TODO: put somewhere public so cause easily accessed
     private static class OpCertificateException
         extends CertificateException
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/package.html b/bcpkix/src/main/java/org/bouncycastle/operator/package.html
deleted file mode 100644
index b64343a..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/operator/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic operators for doing encryption, signing, and digest operations.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
index 79ab492..5379d47 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
@@ -11,8 +11,10 @@
 import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.PBEParameterSpec;
 
+import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
 import org.bouncycastle.asn1.pkcs.PBES2Parameters;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
@@ -23,19 +25,19 @@
 import org.bouncycastle.jcajce.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.ProviderJcaJceHelper;
 import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
-import org.bouncycastle.operator.DefaultSecretKeyProvider;
-import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
+import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
 import org.bouncycastle.operator.InputDecryptor;
 import org.bouncycastle.operator.InputDecryptorProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.SecretKeySizeProvider;
-import org.bouncycastle.operator.jcajce.JceGenericKey;
 
 public class JcePKCSPBEInputDecryptorProviderBuilder
 {
     private JcaJceHelper helper = new DefaultJcaJceHelper();
     private boolean      wrongPKCS12Zero = false;
-    private SecretKeySizeProvider keySizeProvider = DefaultSecretKeyProvider.INSTANCE;
+    private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE;
 
     public JcePKCSPBEInputDecryptorProviderBuilder()
     {
@@ -125,13 +127,31 @@
 
                         SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId());
 
-                        key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+                        if (func.isDefaultPrf())
+                        {
+                            key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+                        }
+                        else
+                        {
+                            key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+                        }
 
                         cipher = helper.createCipher(alg.getEncryptionScheme().getAlgorithm().getId());
 
                         encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
 
-                        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ASN1OctetString.getInstance(alg.getEncryptionScheme().getParameters()).getOctets()));
+                        ASN1Encodable encParams = alg.getEncryptionScheme().getParameters();
+                        if (encParams instanceof ASN1OctetString)
+                        {
+                            cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets()));
+                        }
+                        else
+                        {
+                            // TODO: at the moment it's just GOST, but...
+                            GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams);
+
+                            cipher.init(Cipher.DECRYPT_MODE, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV()));
+                        }
                     }
                 }
                 catch (Exception e)
@@ -150,11 +170,6 @@
                     {
                         return new CipherInputStream(input, cipher);
                     }
-
-                    public GenericKey getKey()
-                    {
-                        return new JceGenericKey(encryptionAlg, key);
-                    }
                 };
             }
         };
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
index b37d2cb..fe53d58 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
@@ -26,7 +26,7 @@
 import org.bouncycastle.jcajce.JcaJceHelper;
 import org.bouncycastle.jcajce.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.ProviderJcaJceHelper;
-import org.bouncycastle.operator.DefaultSecretKeyProvider;
+import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.OutputEncryptor;
@@ -38,7 +38,7 @@
     private ASN1ObjectIdentifier algorithm;
     private ASN1ObjectIdentifier keyEncAlgorithm;
     private SecureRandom random;
-    private SecretKeySizeProvider keySizeProvider = DefaultSecretKeyProvider.INSTANCE;
+    private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE;
 
     public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm)
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/package.html b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/package.html
deleted file mode 100644
index 9b10dc4..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-JCA extensions to the PKCS#10 certification request package.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/package.html b/bcpkix/src/main/java/org/bouncycastle/pkcs/package.html
deleted file mode 100644
index c83de7c..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
-<html>
-<body bgcolor="#ffffff">
-Basic support package for handling and creating PKCS#10 certification requests, PKCS#8 encrypted keys and PKCS#12 keys stores.
-</body>
-</html>
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java
index 9c4d138..2bbf9ea 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/test/PfxPduTest.java
@@ -19,6 +19,7 @@
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.DERBMPString;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.Attribute;
 import org.bouncycastle.asn1.pkcs.ContentInfo;
@@ -455,6 +456,74 @@
       + "LgBvAHIAZzAxMCEwCQYFKw4DAhoFAAQUc8hyg5aq/58lH3whwo66zJkWY28E"
       + "CKHZUIQsQX9hAgIIAA==");
 
+    private byte[] gostPfx = Base64.decode(
+        "MIIHEgIBAzCCBssGCSqGSIb3DQEHAaCCBrwEgga4MIIGtDCCBYEGCSqGSIb3"
+      + "DQEHBqCCBXIwggVuAgEAMIIFZwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBI"
+      + "MCcGCSqGSIb3DQEFDDAaBAi114+lRrpkXAICCAAwCgYGKoUDAgIKBQAwHQYG"
+      + "KoUDAgIVMBMECLEIQPMsz/ZZBgcqhQMCAh8BgIIFAbu13yJiW/BnSKYKbtv9"
+      + "tDJoTv6l9BVpCCI4tvpzJnMeLBJyVZU4JevcJNii+R1LilVuuB+xc8e7/P4G"
+      + "6TILWmnnispr9KPRAbYRfoCJOa59+TYJMur58wwDuYgMapQAFzsvpzyUWi62"
+      + "o3uQbbLKO9hQCeJW2L+K9cbg8k33MjXMLpnblKpqmZbHTmBJDFR3xGw7IEjD"
+      + "UNqruu7DlHY6jctiVJSii9UNEVetSo9AAzfROxRjROg38VsWxLyO9wEMBv/8"
+      + "H8ur+zOtmQPGqirNXmN+pa08OvZin9kh7CgswW03xIbfsdGGGLRAWtvCnEwJ"
+      + "mS2tEfH1SZcuVLpMomhq3FU/jsc12k+vq/jw4I2cmfDL41ieK72bwNj8xUXu"
+      + "JHeoFSPGX4z+nsJUrFbFG4VBuDs2Y0SCWLyYZvdjvJwYjfqtyi/RoFSZjGHF"
+      + "crstf9YNQ0vW0efCJ7pUBH44OrbnCx5ng2U5jFm1b3HBIKA2RX+Tlhv14MgT"
+      + "KSftPZ67eSmgdsyPuQAdMu6fEdBMpVKMNZNRV565690sqi+1jOmH94TUX8XU"
+      + "2pRQj6eGGLq6lgGnnDabcePUEPXW8zW2KYrDKYJ/1QZmVGldvlqnjZMNhIO+"
+      + "Afsqax/P8RBjMduGqdilGdRzbN8PdhVaN0Ys+WzFxiS9gtaA2yPzcQuedWDN"
+      + "T7sIrfIapgFYmmHRQ7ht4AKj+lmOyNadONYw+ww+8RzHB1d2Kk+iXeZCtvH0"
+      + "XFWJZtuoGKSt/gkI0E2vpDfMbLaczaRC7ityO0iJs25ozP4JhZRBVvOmpxc9"
+      + "YuIetbTnTf1TLJKXDgt1IwPZeugbofSeiNv117lx8VgtvMYFD4W+WQlB8HnO"
+      + "C8NOYjkMPElc6PCMB9gGm0cIu1fKLvY8ycLav93JJjdDuC0kgKLb2+8mC5+2"
+      + "DdMkcfgW6hy4c98xnJs8enCww3A4xkRbMU13zMq70liqmKHV2SSurg5hwUHM"
+      + "ZthT8p988ZBrnqW24lXfMBqTK4YtIBMeMnvKocYBXr96ig3GfahI1Aj2Bw2e"
+      + "bpZTVeayYUd+2xX8JJMdqna6Q61AL8/eUhJUETz5+fgQJtPjcKmdJfVHO6nB"
+      + "vOk1t/rjK17eiXLxHCyvfP+Tw8lSFOhcvr4eIeG8WfsWNRu2eKKosOU7uash"
+      + "QpnvQieqDeijuRxf+tbbJ5D86inwbJqdxra7wNuZXmiaB9gFDzNbNjhtL+6i"
+      + "gUyX/iQHKi9bNK+PH6pdH/gkwnG/juhdgqoNY6GRty/LUOPgXD+r5e/ST16R"
+      + "vnlwrlKp5FzRWBEkem+dhelj3rb+cxKEyvPe3TvIUFcmIlV1VCRQ1fBHtX18"
+      + "eC3a3GprH8c40z3S/kdyk7GlFQ27DRLka+iDN05b+MP5jlgvfqYBKxwLfeNu"
+      + "MpxWoCUvYWiQdMih86/l0H+0o5UB8SqRbpuvr6fY910JCk0hDaO1pgB3HlRz"
+      + "k1vb46pg25heXQm3JmO+ghxjOGliYBWjl8p7AfRS9cjS8ca+X02Mv9Viv7Ce"
+      + "3+Gz0MVwfK98viJ3CFxkaEBlM2LM0IeUQbkHG+YwYaTSfl4GYyrug4F0ZdrA"
+      + "KeY9/kIxa/OJxjcIMs2H+2mSpxmrb7ylmHZ2RB8ITiduRVtO091hn/J7N+eT"
+      + "h6BvLBKIFU+UFUdgjxoDNDk7ao++Mu9T3dQfceFBOYzW9vMQgX30yaPLSdan"
+      + "ZMAP0VtiNjCCASsGCSqGSIb3DQEHAaCCARwEggEYMIIBFDCCARAGCyqGSIb3"
+      + "DQEMCgECoIGyMIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAiQ"
+      + "Owewo16xzQICCAAwCgYGKoUDAgIKBQAwHQYGKoUDAgIVMBMECHSCNJJcQ2VI"
+      + "BgcqhQMCAh8BBFYCyRRpFtZgnsxeK7ZHT+aOyoVmzhtnLrqoBHgV4nJJW2/e"
+      + "UcJjc2Rlbzfd+3L/GWcRGF8Bgn+MjiaAqE64Rzaao9t2hc3myw1WrCfPnoEx"
+      + "VI7OPBM5FzFMMCMGCSqGSIb3DQEJFTEWBBTV7LvI27QWRmHD45X2WKXYs3ct"
+      + "AzAlBgkqhkiG9w0BCRQxGB4WAGMAcABfAGUAeABwAG8AcgB0AGUAZDA+MC4w"
+      + "CgYGKoUDAgIJBQAEIJbGZorQsNM63+xozwEI561cTFVCbyHAEEpkvF3eijT8"
+      + "BAgY5sDtkrVeBQICCAA=");
+
+    private byte[] gostPfxFoo123 = Base64.decode(
+        "MIID6gIBAzCCA6MGCSqGSIb3DQEHAaCCA5QEggOQMIIDjDCCApQGCSqGSIb3"
+      + "DQEHBqCCAoUwggKBAgEAMIICegYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBI"
+      + "MCcGCSqGSIb3DQEFDDAaBAhIVrbUVNoQ2wICCAAwCgYGKoUDAgIKBQAwHQYG"
+      + "KoUDAgIVMBMECBLmAh+XCCYhBgcqhQMCAh8BgIICFP9hQLgDq5SORy2npOdo"
+      + "1bvoGl9Qdga1kV9s2c1/Y1kTGpuiYKfm5Il+PurzYdE5t/Wi2+SxoePm/AKA"
+      + "x1Ep5btK/002wnyRbUKdjgF1r7fMXRrd5Ioy8lYxB1v6qhHmzE5fz7FxY+iV"
+      + "Z70dSRS0JkTasI8MRsFLkJJfDb9twgoch8lYGFfYirHLcVy4xxA3JO9VSHm2"
+      + "8nuSWSnsmGN0ufPX14UpV2RFe3Rt0gZ0Jc8u2h2Mo0sIoVU6HVwdXzoe6LN7"
+      + "1NPZdRuhVtjxEvjDAvNJ8WHXQnBQMai2nVAj87uNr6OHLRs+foEccEY9WpPQ"
+      + "GPt4XbPt4MtmVctT2+Gsvf6Ws2UCx6hD4k8i28a6xS8lhTVam2g/2Z5cxtUV"
+      + "HxYt7j13HjuQVsuSNdgtrUnw3l43LnBxRZhlFz0r2zrvTB04ynenS+lGdVuG"
+      + "0TauIH+rdP1ubujw6lFdG9RNgUxWvS5IdwbFGX73a+ZrWiYJeERX11N/6r3g"
+      + "0EqVFNH9t/ROsdAtCCe2FycQoOSb+VxPU6I+SHjwe7Oa7R8Xxazh/eWTsV59"
+      + "QzPuLriUMbyYdQIf4xdclgcJoxFElopgl4orRfzH3XQsVbtTxN33lwjkE0j/"
+      + "686VtcO+b+dU7+BEB7O5yDcx1tupgre0ha/0KOlYfPvmbogGdDf0r6MOwrS7"
+      + "QFXxKlHfp8vn4mNwoy7pjrzjmjclkbkwgfEGCSqGSIb3DQEHAaCB4wSB4DCB"
+      + "3TCB2gYLKoZIhvcNAQwKAQKggaMwgaAwVQYJKoZIhvcNAQUNMEgwJwYJKoZI"
+      + "hvcNAQUMMBoECLD6Ld7TqurqAgIIADAKBgYqhQMCAgoFADAdBgYqhQMCAhUw"
+      + "EwQIoYXV7LETOEAGByqFAwICHwEERyBQK9LuYnOO0ELrge+a6JFeAVwPL85V"
+      + "ip2Kj/GfD3nzZR4tPzCwAt79RriKQklNqa3uCc9o0C9Zk5Qcj36SqiXxD1tz"
+      + "Ea63MSUwIwYJKoZIhvcNAQkVMRYEFKjg5gKM+i+vFhSwaga8YGaZ5thVMD4w"
+      + "LjAKBgYqhQMCAgkFAAQgIwD0CRCwva2Bjdlv5g970H2bCB1aafBNr/hxJLZE"
+      + "Ey4ECAW3VYXJzJpYAgIIAA==");
+
     /**
      * we generate the CA's certificate
      */
@@ -1042,6 +1111,90 @@
         }
     }
 
+    public void testGOST1()
+        throws Exception
+    {
+        char[] password = "1".toCharArray();
+
+        InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder()
+                                                                      .setProvider("BC").build(password);
+        PKCS12PfxPdu pfx = new PKCS12PfxPdu(gostPfx);
+
+        assertTrue(pfx.hasMac());
+        assertTrue(pfx.isMacValid(new JcePKCS12MacCalculatorBuilderProvider().setProvider("BC"), password));
+
+        ContentInfo[] infos = pfx.getContentInfos();
+
+        for (int i = 0; i != infos.length; i++)
+        {
+            if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData))
+            {
+               PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider);
+
+              PKCS12SafeBag[] bags = dataFact.getSafeBags();
+
+                // TODO: finish!
+//                assertEquals(3, bags.length);
+//                assertEquals(PKCSObjectIdentifiers.certBag, bags[0].getType());
+            }
+            else
+            {
+                PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]);
+
+                PKCS12SafeBag[] bags = dataFact.getSafeBags();
+
+                assertEquals(1, bags.length);
+                assertEquals(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag, bags[0].getType());
+
+                PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo)bags[0].getBagValue();
+                PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider);
+                assertEquals(CryptoProObjectIdentifiers.gostR3410_2001, info.getPrivateKeyAlgorithm().getAlgorithm());
+            }
+        }
+    }
+
+    public void testGOST2()
+        throws Exception
+    {
+        char[] password = "foo123".toCharArray();
+
+        InputDecryptorProvider inputDecryptorProvider = new JcePKCSPBEInputDecryptorProviderBuilder()
+                                                                      .setProvider("BC").build(password);
+        PKCS12PfxPdu pfx = new PKCS12PfxPdu(gostPfxFoo123);
+
+        assertTrue(pfx.hasMac());
+        assertTrue(pfx.isMacValid(new JcePKCS12MacCalculatorBuilderProvider().setProvider("BC"), password));
+
+        ContentInfo[] infos = pfx.getContentInfos();
+
+        for (int i = 0; i != infos.length; i++)
+        {
+            if (infos[i].getContentType().equals(PKCSObjectIdentifiers.encryptedData))
+            {
+                PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i], inputDecryptorProvider);
+
+                PKCS12SafeBag[] bags = dataFact.getSafeBags();
+
+                // TODO: finish!
+//                assertEquals(3, bags.length);
+//                assertEquals(PKCSObjectIdentifiers.certBag, bags[0].getType());
+            }
+            else
+            {
+                PKCS12SafeBagFactory dataFact = new PKCS12SafeBagFactory(infos[i]);
+
+                PKCS12SafeBag[] bags = dataFact.getSafeBags();
+
+                assertEquals(1, bags.length);
+                assertEquals(PKCSObjectIdentifiers.pkcs8ShroudedKeyBag, bags[0].getType());
+
+                PKCS8EncryptedPrivateKeyInfo encInfo = (PKCS8EncryptedPrivateKeyInfo)bags[0].getBagValue();
+                PrivateKeyInfo info = encInfo.decryptPrivateKeyInfo(inputDecryptorProvider);
+                assertEquals(CryptoProObjectIdentifiers.gostR3410_2001, info.getPrivateKeyAlgorithm().getAlgorithm());
+            }
+        }
+    }
+
     private X509Certificate[] createCertChain(KeyFactory fact, PublicKey pubKey)
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java
index 76054b9..d757071 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TSPUtil.java
@@ -1,26 +1,17 @@
 package org.bouncycastle.tsp;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.Provider;
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 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.ASN1Set;
 import org.bouncycastle.asn1.cms.Attribute;
 import org.bouncycastle.asn1.cms.AttributeTable;
@@ -35,7 +26,6 @@
 import org.bouncycastle.asn1.x509.Extensions;
 import org.bouncycastle.asn1.x509.ExtensionsGenerator;
 import org.bouncycastle.asn1.x509.KeyPurposeId;
-import org.bouncycastle.asn1.x509.X509Extensions;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.operator.DigestCalculator;
@@ -46,7 +36,6 @@
 
 public class TSPUtil
 {
-    private static Set EMPTY_SET = Collections.unmodifiableSet(new HashSet());
     private static List EMPTY_LIST = Collections.unmodifiableList(new ArrayList());
 
     private static final Map digestLengths = new HashMap();
@@ -82,64 +71,6 @@
         digestNames.put(CryptoProObjectIdentifiers.gostR3411.getId(), "GOST3411");
     }
 
-    /**
-     * Fetches the signature time-stamp attributes from a SignerInformation object.
-     * Checks that the MessageImprint for each time-stamp matches the signature field.
-     * (see RFC 3161 Appendix A).
-     * 
-     * @param signerInfo a SignerInformation to search for time-stamps
-     * @param provider an optional provider to use to create MessageDigest instances
-     * @return a collection of TimeStampToken objects
-     * @throws TSPValidationException
-     * @deprecated use getSignatureTimestamps(SignerInformation, DigestCalculatorProvider)
-     */
-    public static Collection getSignatureTimestamps(SignerInformation signerInfo, Provider provider)
-        throws TSPValidationException
-    {
-        List timestamps = new ArrayList();
-
-        AttributeTable unsignedAttrs = signerInfo.getUnsignedAttributes();
-        if (unsignedAttrs != null)
-        {
-            ASN1EncodableVector allTSAttrs = unsignedAttrs.getAll(
-                PKCSObjectIdentifiers.id_aa_signatureTimeStampToken);
-            for (int i = 0; i < allTSAttrs.size(); ++i)
-            {
-                Attribute tsAttr = (Attribute)allTSAttrs.get(i);            
-                ASN1Set tsAttrValues = tsAttr.getAttrValues();
-                for (int j = 0; j < tsAttrValues.size(); ++j)
-                {
-                    try
-                    {
-                        ContentInfo contentInfo = ContentInfo.getInstance(tsAttrValues.getObjectAt(j));
-                        TimeStampToken timeStampToken = new TimeStampToken(contentInfo);
-                        TimeStampTokenInfo tstInfo = timeStampToken.getTimeStampInfo();
-
-                        MessageDigest digest = createDigestInstance(tstInfo.getMessageImprintAlgOID().getId(), provider);
-                        byte[] expectedDigest = digest.digest(signerInfo.getSignature());
-
-                        if (!Arrays.constantTimeAreEqual(expectedDigest, tstInfo.getMessageImprintDigest()))
-                        {
-                            throw new TSPValidationException("Incorrect digest in message imprint");
-                        }
-
-                        timestamps.add(timeStampToken);
-                    }
-                    catch (NoSuchAlgorithmException e)
-                    {
-                        throw new TSPValidationException("Unknown hash algorithm specified in timestamp");
-                    }
-                    catch (Exception e)
-                    {
-                        throw new TSPValidationException("Timestamp could not be parsed");
-                    }
-                }
-            }
-        }
-
-        return timestamps;
-    }
-
      /**
      * Fetches the signature time-stamp attributes from a SignerInformation object.
      * Checks that the MessageImprint for each time-stamp matches the signature field.
@@ -207,56 +138,9 @@
      * Validate the passed in certificate as being of the correct type to be used
      * for time stamping. To be valid it must have an ExtendedKeyUsage extension
      * which has a key purpose identifier of id-kp-timeStamping.
-     * 
-     * @param cert the certificate of interest.
-     * @throws TSPValidationException if the certicate fails on one of the check points.
-     */
-    public static void validateCertificate(
-        X509Certificate cert)
-        throws TSPValidationException
-    {
-        if (cert.getVersion() != 3)
-        {
-            throw new IllegalArgumentException("Certificate must have an ExtendedKeyUsage extension.");
-        }
-        
-        byte[]  ext = cert.getExtensionValue(X509Extensions.ExtendedKeyUsage.getId());
-        if (ext == null)
-        {
-            throw new TSPValidationException("Certificate must have an ExtendedKeyUsage extension.");
-        }
-        
-        if (!cert.getCriticalExtensionOIDs().contains(X509Extensions.ExtendedKeyUsage.getId()))
-        {
-            throw new TSPValidationException("Certificate must have an ExtendedKeyUsage extension marked as critical.");
-        }
-
-        ASN1InputStream aIn = new ASN1InputStream(new ByteArrayInputStream(ext));
-
-        try
-        {
-            aIn = new ASN1InputStream(new ByteArrayInputStream(((ASN1OctetString)aIn.readObject()).getOctets()));
-            
-            ExtendedKeyUsage    extKey = ExtendedKeyUsage.getInstance(aIn.readObject());
-            
-            if (!extKey.hasKeyPurposeId(KeyPurposeId.id_kp_timeStamping) || extKey.size() != 1)
-            {
-                throw new TSPValidationException("ExtendedKeyUsage not solely time stamping.");
-            }
-        }
-        catch (IOException e)
-        {
-            throw new TSPValidationException("cannot process ExtendedKeyUsage extension");
-        }
-    }
-
-    /**
-     * Validate the passed in certificate as being of the correct type to be used
-     * for time stamping. To be valid it must have an ExtendedKeyUsage extension
-     * which has a key purpose identifier of id-kp-timeStamping.
      *
      * @param cert the certificate of interest.
-     * @throws TSPValidationException if the certicate fails on one of the check points.
+     * @throws TSPValidationException if the certificate fails on one of the check points.
      */
     public static void validateCertificate(
         X509CertificateHolder cert)
@@ -286,23 +170,6 @@
         }
     }
 
-    /*
-     * Return the digest algorithm using one of the standard JCA string
-     * representations rather than the algorithm identifier (if possible).
-     */
-    static String getDigestAlgName(
-        String digestAlgOID)
-    {
-        String digestName = (String)digestNames.get(digestAlgOID);
-
-        if (digestName != null)
-        {
-            return digestName;
-        }
-
-        return digestAlgOID;
-    }
-
     static int getDigestLength(
         String digestAlgOID)
         throws TSPException
@@ -317,47 +184,6 @@
         throw new TSPException("digest algorithm cannot be found.");
     }
 
-    static MessageDigest createDigestInstance(String digestAlgOID, Provider provider)
-        throws NoSuchAlgorithmException
-    {
-        String digestName = TSPUtil.getDigestAlgName(digestAlgOID);
-
-        if (provider != null)
-        {
-            try
-            {
-                return MessageDigest.getInstance(digestName, provider);
-            }
-            catch (NoSuchAlgorithmException e)
-            {
-                // Ignore
-            }
-        }
-
-        return MessageDigest.getInstance(digestName);
-    }
-
-        static Set getCriticalExtensionOIDs(X509Extensions extensions)
-    {
-        if (extensions == null)
-        {
-            return EMPTY_SET;
-        }
-
-        return Collections.unmodifiableSet(new HashSet(java.util.Arrays.asList(extensions.getCriticalExtensionOIDs())));
-    }
-
-    static Set getNonCriticalExtensionOIDs(X509Extensions extensions)
-    {
-        if (extensions == null)
-        {
-            return EMPTY_SET;
-        }
-
-        // TODO: should probably produce a set that imposes correct ordering
-        return Collections.unmodifiableSet(new HashSet(java.util.Arrays.asList(extensions.getNonCriticalExtensionOIDs())));
-    }
-
     static List getExtensionOIDs(Extensions extensions)
     {
         if (extensions == null)
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java
index 8acc41b..696c2d8 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampRequest.java
@@ -4,7 +4,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.math.BigInteger;
-import java.security.NoSuchProviderException;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Enumeration;
@@ -58,9 +57,15 @@
     public TimeStampRequest(InputStream in) 
         throws IOException
     {
+        this(loadRequest(in));
+    }
+
+    private static TimeStampReq loadRequest(InputStream in)
+        throws IOException
+    {
         try
         {
-            this.req = TimeStampReq.getInstance(new ASN1InputStream(in).readObject());
+            return TimeStampReq.getInstance(new ASN1InputStream(in).readObject());
         }
         catch (ClassCastException e)
         {
@@ -126,27 +131,6 @@
     /**
      * Validate the timestamp request, checking the digest to see if it is of an
      * accepted type and whether it is of the correct length for the algorithm specified.
-     * 
-     * @param algorithms a set of String OIDS giving accepted algorithms.
-     * @param policies if non-null a set of policies we are willing to sign under.
-     * @param extensions if non-null a set of extensions we are willing to accept.
-     * @param provider the provider to confirm the digest size against.
-     * @throws TSPException if the request is invalid, or processing fails.
-     * @deprecated use validate method without provider argument.
-     */
-    public void validate(
-        Set     algorithms,
-        Set     policies,
-        Set     extensions,
-        String  provider)
-        throws TSPException, NoSuchProviderException
-    {
-        validate(algorithms, policies, extensions);
-    }
-
-    /**
-     * Validate the timestamp request, checking the digest to see if it is of an
-     * accepted type and whether it is of the correct length for the algorithm specified.
      *
      * @param algorithms a set of OIDs giving accepted algorithms.
      * @param policies if non-null a set of policies OIDs we are willing to sign under.
@@ -228,34 +212,6 @@
         return TSPUtil.getExtensionOIDs(extensions);
     }
 
-    /* (non-Javadoc)
-     * @see java.security.cert.X509Extension#getExtensionValue(java.lang.String)
-     * @deprecated use getExtension(ASN1ObjectIdentifier)
-     */
-    public byte[] getExtensionValue(String oid)
-    {
-        Extensions exts = req.getExtensions();
-
-        if (exts != null)
-        {
-            Extension   ext = exts.getExtension(new ASN1ObjectIdentifier(oid));
-
-            if (ext != null)
-            {
-                try
-                {
-                    return ext.getExtnValue().getEncoded();
-                }
-                catch (Exception e)
-                {
-                    throw new RuntimeException("error encoding " + e.toString());
-                }
-            }
-        }
-
-        return null;
-    }
-
     /**
      * Returns a set of ASN1ObjectIdentifiers giving the non-critical extensions.
      * @return a set of ASN1ObjectIdentifiers.
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java
index 15f5b13..7b7c757 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampResponseGenerator.java
@@ -1,17 +1,13 @@
 package org.bouncycastle.tsp;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.math.BigInteger;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
 import java.util.Date;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.Set;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERInteger;
@@ -142,82 +138,6 @@
     /**
      * Return an appropriate TimeStampResponse.
      * <p>
-     * If genTime is null a timeNotAvailable error response will be returned.
-     *
-     * @param request the request this response is for.
-     * @param serialNumber serial number for the response token.
-     * @param genTime generation time for the response token.
-     * @param provider provider to use for signature calculation.
-     * @deprecated use method that does not require provider
-     * @return
-     * @throws NoSuchAlgorithmException
-     * @throws NoSuchProviderException
-     * @throws TSPException
-     */
-    public TimeStampResponse generate(
-        TimeStampRequest    request,
-        BigInteger          serialNumber,
-        Date                genTime,
-        String              provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, TSPException
-    {   
-        TimeStampResp resp;
-        
-        try
-        {
-            if (genTime == null)
-            {
-                throw new TSPValidationException("The time source is not available.", PKIFailureInfo.timeNotAvailable);
-            }
-
-            request.validate(acceptedAlgorithms, acceptedPolicies, acceptedExtensions, provider);
-
-            status = PKIStatus.GRANTED;
-            this.addStatusString("Operation Okay");
-            
-            PKIStatusInfo pkiStatusInfo = getPKIStatusInfo();
-            
-            ContentInfo tstTokenContentInfo = null;
-            try
-            {
-                ByteArrayInputStream    bIn = new ByteArrayInputStream(tokenGenerator.generate(request, serialNumber, genTime, provider).toCMSSignedData().getEncoded());
-                ASN1InputStream         aIn = new ASN1InputStream(bIn);
-                
-                tstTokenContentInfo = ContentInfo.getInstance(aIn.readObject());
-            }
-            catch (java.io.IOException ioEx)
-            {
-                throw new TSPException(
-                        "Timestamp token received cannot be converted to ContentInfo", ioEx);
-            }
-    
-            resp = new TimeStampResp(pkiStatusInfo, tstTokenContentInfo);
-        }
-        catch (TSPValidationException e)
-        {
-            status = PKIStatus.REJECTION;
-            
-            this.setFailInfoField(e.getFailureCode());
-            this.addStatusString(e.getMessage());
-            
-            PKIStatusInfo pkiStatusInfo = getPKIStatusInfo();
-
-            resp = new TimeStampResp(pkiStatusInfo, null);
-        }
-
-        try
-        {
-            return new TimeStampResponse(resp);
-        }
-        catch (IOException e)
-        {
-            throw new TSPException("created badly formatted response!");
-        }
-    }
-
-    /**
-     * Return an appropriate TimeStampResponse.
-     * <p>
      * If genTime is null a timeNotAvailable error response will be returned. Calling generate() is the
      * equivalent of:
      * <pre>
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java
index bc4a631..0422998 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampToken.java
@@ -4,14 +4,6 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.cert.CertStore;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateExpiredException;
-import java.security.cert.CertificateNotYetValidException;
-import java.security.cert.X509Certificate;
 import java.util.Collection;
 import java.util.Date;
 
@@ -32,7 +24,6 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.IssuerSerial;
-import org.bouncycastle.asn1.x509.X509Name;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSProcessable;
@@ -40,13 +31,14 @@
 import org.bouncycastle.cms.SignerId;
 import org.bouncycastle.cms.SignerInformation;
 import org.bouncycastle.cms.SignerInformationVerifier;
-import org.bouncycastle.jce.PrincipalUtil;
-import org.bouncycastle.jce.X509Principal;
 import org.bouncycastle.operator.DigestCalculator;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Store;
 
+/**
+ * Carrier class for a TimeStampToken.
+ */
 public class TimeStampToken
 {
     CMSSignedData tsToken;
@@ -158,17 +150,6 @@
         return tsaSignerInfo.getUnsignedAttributes();
     }
 
-    /**
-     * @deprecated use getCertificates() or getCRLs()
-     */
-    public CertStore getCertificatesAndCRLs(
-        String type,
-        String provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, CMSException
-    {
-        return tsToken.getCertificatesAndCRLs(type, provider);
-    }
-
     public Store getCertificates()
     {
         return tsToken.getCertificates();
@@ -188,90 +169,6 @@
      * Validate the time stamp token.
      * <p>
      * To be valid the token must be signed by the passed in certificate and
-     * the certificate must be the one referred to by the SigningCertificate 
-     * attribute included in the hashed attributes of the token. The
-     * certificate must also have the ExtendedKeyUsageExtension with only
-     * KeyPurposeId.id_kp_timeStamping and have been valid at the time the
-     * timestamp was created.
-     * </p>
-     * <p>
-     * A successful call to validate means all the above are true.
-     * </p>
-     * @deprecated
-     */
-    public void validate(
-        X509Certificate cert,
-        String provider)
-        throws TSPException, TSPValidationException,
-        CertificateExpiredException, CertificateNotYetValidException, NoSuchProviderException
-    {
-        try
-        {
-            if (!Arrays.constantTimeAreEqual(certID.getCertHash(), MessageDigest.getInstance(certID.getHashAlgorithmName()).digest(cert.getEncoded())))
-            {
-                throw new TSPValidationException("certificate hash does not match certID hash.");
-            }
-            
-            if (certID.getIssuerSerial() != null)
-            {
-                if (!certID.getIssuerSerial().getSerial().getValue().equals(cert.getSerialNumber()))
-                {
-                    throw new TSPValidationException("certificate serial number does not match certID for signature.");
-                }
-                
-                GeneralName[]   names = certID.getIssuerSerial().getIssuer().getNames();
-                X509Principal   principal = PrincipalUtil.getIssuerX509Principal(cert);
-                boolean         found = false;
-                
-                for (int i = 0; i != names.length; i++)
-                {
-                    if (names[i].getTagNo() == 4 && new X509Principal(X509Name.getInstance(names[i].getName())).equals(principal))
-                    {
-                        found = true;
-                        break;
-                    }
-                }
-                
-                if (!found)
-                {
-                    throw new TSPValidationException("certificate name does not match certID for signature. ");
-                }
-            }
-            
-            TSPUtil.validateCertificate(cert);
-            
-            cert.checkValidity(tstInfo.getGenTime());
-
-            if (!tsaSignerInfo.verify(cert, provider))
-            {
-                throw new TSPValidationException("signature not created by certificate.");
-            }
-        }
-        catch (CMSException e)
-        {
-            if (e.getUnderlyingException() != null)
-            {
-                throw new TSPException(e.getMessage(), e.getUnderlyingException());
-            }
-            else
-            {
-                throw new TSPException("CMS exception: " + e, e);
-            }
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            throw new TSPException("cannot find algorithm: " + e, e);
-        }
-        catch (CertificateEncodingException e)
-        {
-            throw new TSPException("problem processing certificate: " + e, e);
-        }
-    }
-
-    /**
-     * Validate the time stamp token.
-     * <p>
-     * To be valid the token must be signed by the passed in certificate and
      * the certificate must be the one referred to by the SigningCertificate
      * attribute included in the hashed attributes of the token. The
      * certificate must also have the ExtendedKeyUsageExtension with only
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java
index 1a1cec1..008ca95 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/TimeStampTokenGenerator.java
@@ -1,26 +1,10 @@
 package org.bouncycastle.tsp;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.OutputStream;
 import java.math.BigInteger;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
-import java.security.NoSuchProviderException;
-import java.security.PrivateKey;
-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.security.interfaces.DSAPrivateKey;
-import java.security.interfaces.RSAPrivateKey;
 import java.util.ArrayList;
-import java.util.Collection;
 import java.util.Date;
-import java.util.Hashtable;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -30,8 +14,6 @@
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERSet;
-import org.bouncycastle.asn1.cms.Attribute;
 import org.bouncycastle.asn1.cms.AttributeTable;
 import org.bouncycastle.asn1.ess.ESSCertID;
 import org.bouncycastle.asn1.ess.ESSCertIDv2;
@@ -44,24 +26,17 @@
 import org.bouncycastle.asn1.tsp.TSTInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.GeneralName;
-import org.bouncycastle.cert.jcajce.JcaX509CRLHolder;
-import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.asn1.x509.GeneralNames;
+import org.bouncycastle.asn1.x509.IssuerSerial;
+import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cms.CMSAttributeTableGenerationException;
 import org.bouncycastle.cms.CMSAttributeTableGenerator;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSProcessableByteArray;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.cms.CMSSignedDataGenerator;
-import org.bouncycastle.cms.CMSSignedGenerator;
-import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
 import org.bouncycastle.cms.SignerInfoGenerator;
-import org.bouncycastle.cms.SimpleAttributeTableGenerator;
-import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
-import org.bouncycastle.jce.interfaces.GOST3410PrivateKey;
 import org.bouncycastle.operator.DigestCalculator;
-import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
-import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
 import org.bouncycastle.util.CollectionStore;
 import org.bouncycastle.util.Store;
 
@@ -105,12 +80,6 @@
     
     private ASN1ObjectIdentifier  tsaPolicyOID;
 
-    PrivateKey      key;
-    X509Certificate cert;
-    String          digestOID;
-    AttributeTable  signedAttr;
-    AttributeTable  unsignedAttr;
-
     private List certs = new ArrayList();
     private List crls = new ArrayList();
     private List attrCerts = new ArrayList();
@@ -134,6 +103,29 @@
         ASN1ObjectIdentifier            tsaPolicy)
         throws IllegalArgumentException, TSPException
     {
+        this(signerInfoGen, digestCalculator, tsaPolicy, false);
+    }
+
+    /**
+     * Basic Constructor - set up a calculator based on signerInfoGen with a ESSCertID calculated from
+     * the signer's associated certificate using the sha1DigestCalculator. If alternate values are required
+     * for id-aa-signingCertificate they should be added to the signerInfoGen object before it is passed in,
+     * otherwise a standard digest based value will be added.
+     *
+     * @param signerInfoGen the generator for the signer we are using.
+     * @param digestCalculator calculator for to use for digest of certificate.
+     * @param tsaPolicy tasPolicy to send.
+     * @param isIssuerSerialIncluded should issuerSerial be included in the ESSCertIDs, true if yes, by default false.
+     * @throws IllegalArgumentException if calculator is not SHA-1 or there is no associated certificate for the signer,
+     * @throws TSPException if the signer certificate cannot be processed.
+     */
+    public TimeStampTokenGenerator(
+        final SignerInfoGenerator       signerInfoGen,
+        DigestCalculator                digestCalculator,
+        ASN1ObjectIdentifier            tsaPolicy,
+        boolean                         isIssuerSerialIncluded)
+        throws IllegalArgumentException, TSPException
+    {
         this.signerInfoGen = signerInfoGen;
         this.tsaPolicyOID = tsaPolicy;
 
@@ -142,19 +134,22 @@
             throw new IllegalArgumentException("SignerInfoGenerator must have an associated certificate");
         }
 
-        TSPUtil.validateCertificate(signerInfoGen.getAssociatedCertificate());
+        X509CertificateHolder assocCert = signerInfoGen.getAssociatedCertificate();
+        TSPUtil.validateCertificate(assocCert);
 
         try
         {
             OutputStream dOut = digestCalculator.getOutputStream();
 
-            dOut.write(signerInfoGen.getAssociatedCertificate().getEncoded());
+            dOut.write(assocCert.getEncoded());
 
             dOut.close();
 
             if (digestCalculator.getAlgorithmIdentifier().getAlgorithm().equals(OIWObjectIdentifiers.idSHA1))
             {
-                final ESSCertID essCertid = new ESSCertID(digestCalculator.getDigest());
+                final ESSCertID essCertid = new ESSCertID(digestCalculator.getDigest(),
+                                            isIssuerSerialIncluded ? new IssuerSerial(new GeneralNames(new GeneralName(assocCert.getIssuer())), assocCert.getSerialNumber())
+                                                                   : null);
 
                 this.signerInfoGen = new SignerInfoGenerator(signerInfoGen, new CMSAttributeTableGenerator()
                 {
@@ -175,7 +170,9 @@
             else
             {
                 AlgorithmIdentifier digAlgID = new AlgorithmIdentifier(digestCalculator.getAlgorithmIdentifier().getAlgorithm());
-                final ESSCertIDv2   essCertid = new ESSCertIDv2(digAlgID, digestCalculator.getDigest());
+                final ESSCertIDv2   essCertid = new ESSCertIDv2(digAlgID, digestCalculator.getDigest(),
+                                                    isIssuerSerialIncluded ? new IssuerSerial(new GeneralNames(new GeneralName(assocCert.getIssuer())), new ASN1Integer(assocCert.getSerialNumber()))
+                                                                           : null);
 
                 this.signerInfoGen = new SignerInfoGenerator(signerInfoGen, new CMSAttributeTableGenerator()
                 {
@@ -201,185 +198,6 @@
     }
 
     /**
-     * Basic Constructor - set up a calculator based on signerInfoGen with a ESSCertID calculated from
-     * the signer's associated certificate using the sha1DigestCalculator.
-     *
-     * @param sha1DigestCalculator calculator for SHA-1 of certificate.
-     * @param signerInfoGen the generator for the signer we are using.
-     * @param tsaPolicy tasPolicy to send.
-     * @throws IllegalArgumentException if calculator is not SHA-1 or there is no associated certificate for the signer,
-     * @throws TSPException if the signer certificate cannot be processed.
-     * @deprecated use constructor taking signerInfoGen first.
-     */
-    public TimeStampTokenGenerator(
-        DigestCalculator sha1DigestCalculator,
-        final SignerInfoGenerator         signerInfoGen,
-        ASN1ObjectIdentifier              tsaPolicy)
-        throws IllegalArgumentException, TSPException
-    {
-        this(signerInfoGen, sha1DigestCalculator, tsaPolicy);
-    }
-
-    /**
-     * basic creation - only the default attributes will be included here.
-     * @deprecated use SignerInfoGenerator constructor that takes a digest calculator
-     */
-    public TimeStampTokenGenerator(
-        final SignerInfoGenerator     signerInfoGen,
-        ASN1ObjectIdentifier          tsaPolicy)
-        throws IllegalArgumentException, TSPException
-    {
-        this(new DigestCalculator()
-        {
-            private ByteArrayOutputStream bOut = new ByteArrayOutputStream();
-
-            public AlgorithmIdentifier getAlgorithmIdentifier()
-            {
-                return new AlgorithmIdentifier(OIWObjectIdentifiers.idSHA1, DERNull.INSTANCE);
-            }
-
-            public OutputStream getOutputStream()
-            {
-                return bOut;
-            }
-
-            public byte[] getDigest()
-            {
-                try
-                {
-                    return MessageDigest.getInstance("SHA-1").digest(bOut.toByteArray());
-                }
-                catch (NoSuchAlgorithmException e)
-                {
-                    throw new IllegalStateException("cannot find sha-1: "+ e.getMessage());
-                }
-            }
-        }, signerInfoGen, tsaPolicy);
-    }
-
-    /**
-     * basic creation - only the default attributes will be included here.
-     * @deprecated use SignerInfoGenerator constructor that takes a digest calculator.
-     */
-    public TimeStampTokenGenerator(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          digestOID,
-        String          tsaPolicyOID)
-        throws IllegalArgumentException, TSPException
-    {
-        this(key, cert, digestOID, tsaPolicyOID, null, null);
-    }
-
-    /**
-     * basic creation - only the default attributes will be included here.
-     * @deprecated use SignerInfoGenerator constructor that takes a digest calculator.
-     */
-    public TimeStampTokenGenerator(
-        PrivateKey      key,
-        X509Certificate cert,
-        ASN1ObjectIdentifier          digestOID,
-        String          tsaPolicyOID)
-        throws IllegalArgumentException, TSPException
-    {
-        this(key, cert, digestOID.getId(), tsaPolicyOID, null, null);
-    }
-
-    /**
-     * create with a signer with extra signed/unsigned attributes.
-     * @deprecated use SignerInfoGenerator constructor that takes a digest calculator.
-     */
-    public TimeStampTokenGenerator(
-        PrivateKey      key,
-        X509Certificate cert,
-        String          digestOID,
-        String          tsaPolicyOID,
-        AttributeTable  signedAttr,
-        AttributeTable  unsignedAttr)
-        throws IllegalArgumentException, TSPException
-    {   
-        this.key = key;
-        this.cert = cert;
-        this.digestOID = digestOID;
-        this.tsaPolicyOID = new ASN1ObjectIdentifier(tsaPolicyOID);
-        this.unsignedAttr = unsignedAttr;
-
-        //
-        // add the essCertid
-        //
-        Hashtable signedAttrs = null;
-        
-        if (signedAttr != null)
-        {
-            signedAttrs = signedAttr.toHashtable();
-        }
-        else
-        {
-            signedAttrs = new Hashtable();
-        }
-
-
-        TSPUtil.validateCertificate(cert);
-
-        try
-        {
-            ESSCertID essCertid = new ESSCertID(MessageDigest.getInstance("SHA-1").digest(cert.getEncoded()));
-            signedAttrs.put(PKCSObjectIdentifiers.id_aa_signingCertificate,
-                    new Attribute(
-                            PKCSObjectIdentifiers.id_aa_signingCertificate,
-                            new DERSet(new SigningCertificate(essCertid))));
-        }
-        catch (NoSuchAlgorithmException e)
-        {
-            throw new TSPException("Can't find a SHA-1 implementation.", e);
-        }
-        catch (CertificateEncodingException e)
-        {
-            throw new TSPException("Exception processing certificate.", e);
-        }
-        
-        this.signedAttr = new AttributeTable(signedAttrs);
-    }
-
-    /**
-     * @deprecated use addCertificates and addCRLs
-     * @param certificates
-     * @throws CertStoreException
-     * @throws TSPException
-     */
-    public void setCertificatesAndCRLs(CertStore certificates)
-            throws CertStoreException, TSPException
-    {
-        Collection c1 = certificates.getCertificates(null);
-
-        for (Iterator it = c1.iterator(); it.hasNext();)
-        {
-            try
-            {
-                certs.add(new JcaX509CertificateHolder((X509Certificate)it.next()));
-            }
-            catch (CertificateEncodingException e)
-            {
-                throw new TSPException("cannot encode certificate: " + e.getMessage(), e);
-            }
-        }
-
-        c1 = certificates.getCRLs(null);
-
-        for (Iterator it = c1.iterator(); it.hasNext();)
-        {
-            try
-            {
-                crls.add(new JcaX509CRLHolder((X509CRL)it.next()));
-            }
-            catch (CRLException e)
-            {
-                throw new TSPException("cannot encode CRL: " + e.getMessage(), e);
-            }
-        }
-    }
-
-    /**
      * Add the store of X509 Certificates to the generator.
      *
      * @param certStore  a Store containing X509CertificateHolder objects
@@ -434,55 +252,22 @@
     {
         this.tsa = tsa;
     }
-    
-    //------------------------------------------------------------------------------
 
-    public TimeStampToken generate(
-        TimeStampRequest    request,
-        BigInteger          serialNumber,
-        Date                genTime,
-        String              provider)
-        throws NoSuchAlgorithmException, NoSuchProviderException, TSPException
-    {
-        if (signerInfoGen == null)
-        {
-            try
-            {
-                JcaSignerInfoGeneratorBuilder sigBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(provider).build());
-
-                sigBuilder.setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(signedAttr));
-
-                if (unsignedAttr != null)
-                {
-                    sigBuilder.setUnsignedAttributeGenerator(new SimpleAttributeTableGenerator(unsignedAttr));
-                }
-
-                signerInfoGen = sigBuilder.build(new JcaContentSignerBuilder(getSigAlgorithm(key, digestOID)).setProvider(provider).build(key), cert);
-            }
-            catch (OperatorCreationException e)
-            {
-                throw new TSPException("Error generating signing operator", e);
-            }
-            catch (CertificateEncodingException e)
-            {
-                throw new TSPException("Error encoding certificate", e);
-            }
-        }
-
-        return generate(request, serialNumber, genTime);
-    }
-
+    /**
+     * Generate a TimeStampToken for the passed in request and serialNumber marking it with the passed in genTime.
+     *
+     * @param request the originating request.
+     * @param serialNumber serial number for the TimeStampToken
+     * @param genTime token generation time.
+     * @return a TimeStampToken
+     * @throws TSPException
+     */
     public TimeStampToken generate(
         TimeStampRequest    request,
         BigInteger          serialNumber,
         Date                genTime)
         throws TSPException
     {
-        if (signerInfoGen == null)
-        {
-            throw new IllegalStateException("can only use this method with SignerInfoGenerator constructor");
-        }
-
         ASN1ObjectIdentifier digestAlgOID = request.getMessageImprintAlgOID();
 
         AlgorithmIdentifier algID = new AlgorithmIdentifier(digestAlgOID, DERNull.INSTANCE);
@@ -568,34 +353,4 @@
             throw new TSPException("Exception encoding info", e);
         }
     }
-
-    private String getSigAlgorithm(
-        PrivateKey key,
-        String     digestOID)
-    {
-        String enc = null;
-
-        if (key instanceof RSAPrivateKey || "RSA".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            enc = "RSA";
-        }
-        else if (key instanceof DSAPrivateKey || "DSA".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            enc = "DSA";
-        }
-        else if ("ECDSA".equalsIgnoreCase(key.getAlgorithm()) || "EC".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            enc = "ECDSA";
-        }
-        else if (key instanceof GOST3410PrivateKey || "GOST3410".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            enc = "GOST3410";
-        }
-        else if ("ECGOST3410".equalsIgnoreCase(key.getAlgorithm()))
-        {
-            enc = CMSSignedGenerator.ENCRYPTION_ECGOST3410;
-        }
-
-        return TSPUtil.getDigestAlgName(digestOID) + "with" + enc;
-    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/cms/package.html b/bcpkix/src/main/java/org/bouncycastle/tsp/cms/package.html
deleted file mode 100644
index 2cf1bac..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/cms/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for dealing Syntax for Binding Documents with Time-Stamps - RFC 5544.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/package.html b/bcpkix/src/main/java/org/bouncycastle/tsp/package.html
deleted file mode 100644
index 45d0c3c..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for dealing Time Stamp Protocol (TSP) - RFC 3161.
-</body>
-</html>
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/AllTests.java
index 87d4688..19fc664 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/AllTests.java
@@ -22,7 +22,6 @@
         TestSuite suite = new TestSuite("TSP Tests");
         
         suite.addTestSuite(ParseTest.class);
-        suite.addTestSuite(TSPTest.class);
         suite.addTestSuite(NewTSPTest.class);
         suite.addTestSuite(CMSTimeStampedDataTest.class);
         suite.addTestSuite(CMSTimeStampedDataParserTest.class);
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/CMSTimeStampedDataGeneratorTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/CMSTimeStampedDataGeneratorTest.java
index e274dc0..a9ab7db 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/test/CMSTimeStampedDataGeneratorTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/CMSTimeStampedDataGeneratorTest.java
@@ -81,7 +81,7 @@
         throws Exception
     {
         BcDigestCalculatorProvider calculatorProvider = new BcDigestCalculatorProvider();
-        String algOID = "2.16.840.1.101.3.4.2.1"; // SHA-256
+        ASN1ObjectIdentifier algOID = new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.1"); // SHA-256
         DigestCalculator hashCalculator = calculatorProvider.get(new AlgorithmIdentifier(algOID));
 
         cmsTimeStampedDataGenerator.initialiseMessageImprintDigestCalculator(hashCalculator);
@@ -125,7 +125,7 @@
         cmsTimeStampedDataGenerator.setMetaData(true, fileInput, "TXT");
 
         BcDigestCalculatorProvider calculatorProvider = new BcDigestCalculatorProvider();
-        String algOID = "2.16.840.1.101.3.4.2.1"; // SHA-256
+        ASN1ObjectIdentifier algOID = new ASN1ObjectIdentifier("2.16.840.1.101.3.4.2.1"); // SHA-256
         DigestCalculator hashCalculator = calculatorProvider.get(new AlgorithmIdentifier(algOID));
 
         cmsTimeStampedDataGenerator.initialiseMessageImprintDigestCalculator(hashCalculator);
@@ -136,7 +136,7 @@
         TimeStampToken timeStampToken = createTimeStampToken(hashCalculator.getDigest(), NISTObjectIdentifiers.id_sha256);
         CMSTimeStampedData cmsTimeStampedData = cmsTimeStampedDataGenerator.generate(timeStampToken, baseData);
 
-        for (int i = 0; i < 3; i++)
+        for (int i = 0; i <= 3; i++)
         {
             byte[] newRequestData = cmsTimeStampedData.calculateNextHash(hashCalculator);
             TimeStampToken newTimeStampToken = createTimeStampToken(newRequestData, NISTObjectIdentifiers.id_sha256);
@@ -167,7 +167,7 @@
 
         CMSTimeStampedData cmsTimeStampedData = cmsTimeStampedDataGenerator.generate(timeStampToken, baseData);
 
-        for (int i = 0; i < 3; i++) {
+        for (int i = 0; i <= 3; i++) {
             switch (i) {
             case 0:
                 algIdentifier =    NISTObjectIdentifiers.id_sha224;
@@ -213,7 +213,7 @@
         byte[] digest = imprintCalculator.getDigest();
 
         TimeStampToken[] tokens = cmsTspData.getTimeStampTokens();
-        assertEquals("TimeStampToken expected and verified are different", 4, tokens.length);
+        assertEquals("TimeStampToken expected and verified are different", 5, tokens.length);
         for (int i = 0; i < tokens.length; i++)
         {
             cmsTspData.validate(newCalculatorProvider, digest, tokens[i]);
@@ -243,7 +243,7 @@
         byte[] digest = imprintCalculator.getDigest();
 
         TimeStampToken[] tokens = cmsTspData.getTimeStampTokens();
-        assertEquals("TimeStampToken expected and verified are different", 4, tokens.length);
+        assertEquals("TimeStampToken expected and verified are different", 5, tokens.length);
         for (int i = 0; i < tokens.length; i++)
         {
             cmsTspData.validate(newCalculatorProvider, digest, tokens[i]);
@@ -291,7 +291,7 @@
 
 
         TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-            new JcaSimpleSignerInfoGeneratorBuilder().build(algorithmName, privateKey, cert), new ASN1ObjectIdentifier("1.2"));
+            new JcaSimpleSignerInfoGeneratorBuilder().build(algorithmName, privateKey, cert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2"));
 
         tsTokenGen.addCertificates(certs);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java
index 7f69e6e..7bf19be 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/NewTSPTest.java
@@ -724,7 +724,7 @@
 
         TimeStampToken  tsToken = tsResp.getTimeStampToken();
 
-        tsToken.validate(cert, "BC");
+        tsToken.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
         
         //
         // check validation
@@ -771,7 +771,7 @@
     {
         JcaSignerInfoGeneratorBuilder infoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().setProvider(BC).build());
 
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(privateKey), cert), new ASN1ObjectIdentifier("1.2.3"));
+        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(infoGeneratorBuilder.build(new JcaContentSignerBuilder("MD5withRSA").setProvider(BC).build(privateKey), cert), new SHA1DigestCalculator(), new ASN1ObjectIdentifier("1.2.3"));
 
         tsTokenGen.addCertificates(certs);
         
@@ -788,7 +788,7 @@
 
         TimeStampToken  tsToken = tsResp.getTimeStampToken();
 
-        tsToken.validate(cert, "BC");
+        tsToken.validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
         
         //
         // check validation
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/ParseTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/ParseTest.java
index d94bfb7..557d012 100644
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/test/ParseTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/tsp/test/ParseTest.java
@@ -299,7 +299,7 @@
         
         try
         {
-            req.validate(TSPAlgorithms.ALLOWED, null, null, "BC");
+            req.validate(TSPAlgorithms.ALLOWED, null, null);
         }
         catch (Exception e)
         {
@@ -327,7 +327,7 @@
 
         resp.validate(req);
 
-        resp.getTimeStampToken().validate(cert, "BC");
+        resp.getTimeStampToken().validate(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert));
     }
     
     private void unacceptableResponseParse(
diff --git a/bcpkix/src/main/java/org/bouncycastle/tsp/test/TSPTest.java b/bcpkix/src/main/java/org/bouncycastle/tsp/test/TSPTest.java
deleted file mode 100644
index f0d635d..0000000
--- a/bcpkix/src/main/java/org/bouncycastle/tsp/test/TSPTest.java
+++ /dev/null
@@ -1,603 +0,0 @@
-package org.bouncycastle.tsp.test;
-
-import java.math.BigInteger;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.cert.CertStore;
-import java.security.cert.CollectionCertStoreParameters;
-import java.security.cert.X509Certificate;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-
-import junit.framework.TestCase;
-import org.bouncycastle.asn1.cmp.PKIFailureInfo;
-import org.bouncycastle.asn1.cms.AttributeTable;
-import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.tsp.GenTimeAccuracy;
-import org.bouncycastle.tsp.TSPAlgorithms;
-import org.bouncycastle.tsp.TSPValidationException;
-import org.bouncycastle.tsp.TimeStampRequest;
-import org.bouncycastle.tsp.TimeStampRequestGenerator;
-import org.bouncycastle.tsp.TimeStampResponse;
-import org.bouncycastle.tsp.TimeStampResponseGenerator;
-import org.bouncycastle.tsp.TimeStampToken;
-import org.bouncycastle.tsp.TimeStampTokenGenerator;
-import org.bouncycastle.tsp.TimeStampTokenInfo;
-import org.bouncycastle.util.Arrays;
-
-public class TSPTest
-    extends TestCase
-{
-    public void testGeneral()
-        throws Exception
-    {
-            String signDN = "O=Bouncy Castle, C=AU";
-            KeyPair signKP = TSPTestUtil.makeKeyPair();
-            X509Certificate signCert = TSPTestUtil.makeCACertificate(signKP,
-                    signDN, signKP, signDN);
-
-            String origDN = "CN=Eric H. Echidna, E=eric@bouncycastle.org, O=Bouncy Castle, C=AU";
-            KeyPair origKP = TSPTestUtil.makeKeyPair();
-            X509Certificate origCert = TSPTestUtil.makeCertificate(origKP,
-                    origDN, signKP, signDN);
-
-
-            
-            List certList = new ArrayList();
-            certList.add(origCert);
-            certList.add(signCert);
-
-            CertStore certs = CertStore.getInstance("Collection",
-                    new CollectionCertStoreParameters(certList), "BC");
-            
-            basicTest(origKP.getPrivate(), origCert, certs);     
-            responseValidationTest(origKP.getPrivate(), origCert, certs);
-            incorrectHashTest(origKP.getPrivate(), origCert, certs);
-            badAlgorithmTest(origKP.getPrivate(), origCert, certs);
-            timeNotAvailableTest(origKP.getPrivate(), origCert, certs);
-            badPolicyTest(origKP.getPrivate(), origCert, certs);
-            tokenEncodingTest(origKP.getPrivate(), origCert, certs);
-            certReqTest(origKP.getPrivate(), origCert, certs);
-            testAccuracyZeroCerts(origKP.getPrivate(), origCert, certs);
-            testAccuracyWithCertsAndOrdering(origKP.getPrivate(), origCert, certs);
-            testNoNonse(origKP.getPrivate(), origCert, certs);
-    }
-    
-    private void basicTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.SHA1, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest          request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        tsToken.validate(cert, "BC");
-
-        AttributeTable  table = tsToken.getSignedAttributes();
-
-        assertNotNull("no signingCertificate attribute found", table.get(PKCSObjectIdentifiers.id_aa_signingCertificate));
-    }
-    
-    private void responseValidationTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.MD5, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest          request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        tsToken.validate(cert, "BC");
-        
-        //
-        // check validation
-        //
-        tsResp.validate(request);
-        
-        try
-        {
-            request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(101));
-            
-            tsResp.validate(request);
-            
-            fail("response validation failed on invalid nonce.");
-        }
-        catch (TSPValidationException e)
-        {
-            // ignore
-        }
-
-        try
-        {
-            request = reqGen.generate(TSPAlgorithms.SHA1, new byte[22], BigInteger.valueOf(100));
-            
-            tsResp.validate(request);
-            
-            fail("response validation failed on wrong digest.");
-        }
-        catch (TSPValidationException e)
-        {
-            // ignore
-        }
-        
-        try
-        {
-            request = reqGen.generate(TSPAlgorithms.MD5, new byte[20], BigInteger.valueOf(100));
-            
-            tsResp.validate(request);
-            
-            fail("response validation failed on wrong digest.");
-        }
-        catch (TSPValidationException e)
-        {
-            // ignore
-        }
-    }
-    
-    private void incorrectHashTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.SHA1, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest            request = reqGen.generate(TSPAlgorithms.SHA1, new byte[16]);
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        if (tsToken != null)
-        {
-            fail("incorrectHash - token not null.");
-        }
-        
-        PKIFailureInfo  failInfo = tsResp.getFailInfo();
-        
-        if (failInfo == null)
-        {
-            fail("incorrectHash - failInfo set to null.");
-        }
-        
-        if (failInfo.intValue() != PKIFailureInfo.badDataFormat)
-        {
-            fail("incorrectHash - wrong failure info returned.");
-        }
-    }
-    
-    private void badAlgorithmTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.SHA1, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest            request = reqGen.generate("1.2.3.4.5", new byte[20]);
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        if (tsToken != null)
-        {
-            fail("badAlgorithm - token not null.");
-        }
-
-        PKIFailureInfo  failInfo = tsResp.getFailInfo();
-        
-        if (failInfo == null)
-        {
-            fail("badAlgorithm - failInfo set to null.");
-        }
-        
-        if (failInfo.intValue() != PKIFailureInfo.badAlg)
-        {
-            fail("badAlgorithm - wrong failure info returned.");
-        }
-    }
-
-    private void timeNotAvailableTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.SHA1, "1.2");
-
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest            request = reqGen.generate("1.2.3.4.5", new byte[20]);
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), null, "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        if (tsToken != null)
-        {
-            fail("timeNotAvailable - token not null.");
-        }
-
-        PKIFailureInfo  failInfo = tsResp.getFailInfo();
-
-        if (failInfo == null)
-        {
-            fail("timeNotAvailable - failInfo set to null.");
-        }
-
-        if (failInfo.intValue() != PKIFailureInfo.timeNotAvailable)
-        {
-            fail("timeNotAvailable - wrong failure info returned.");
-        }
-    }
-
-    private void badPolicyTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.SHA1, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        
-        reqGen.setReqPolicy("1.1");
-        
-        TimeStampRequest            request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20]);
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED, new HashSet());
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        if (tsToken != null)
-        {
-            fail("badPolicy - token not null.");
-        }
-
-        PKIFailureInfo  failInfo = tsResp.getFailInfo();
-        
-        if (failInfo == null)
-        {
-            fail("badPolicy - failInfo set to null.");
-        }
-        
-        if (failInfo.intValue() != PKIFailureInfo.unacceptedPolicy)
-        {
-            fail("badPolicy - wrong failure info returned.");
-        }
-    }
-    
-    private void certReqTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.MD5, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-        
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        
-        //
-        // request with certReq false
-        //
-        reqGen.setCertReq(false);
-        
-        TimeStampRequest          request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-        
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-        
-        assertNull(tsToken.getTimeStampInfo().getGenTimeAccuracy());  // check for abscence of accuracy
-        
-        assertEquals("1.2", tsToken.getTimeStampInfo().getPolicy().getId());
-        
-        try
-        {
-            tsToken.validate(cert, "BC");
-        }
-        catch (TSPValidationException e)
-        {
-            fail("certReq(false) verification of token failed.");
-        }
-
-        CertStore   respCerts = tsToken.getCertificatesAndCRLs("Collection", "BC");
-        
-        Collection  certsColl = respCerts.getCertificates(null);
-        
-        if (!certsColl.isEmpty())
-        {
-            fail("certReq(false) found certificates in response.");
-        }
-    }
-    
-    
-    private void tokenEncodingTest(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.SHA1, "1.2.3.4.5.6");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        TimeStampRequestGenerator  reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest           request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-        TimeStampResponse          tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampResponse tsResponse = new TimeStampResponse(tsResp.getEncoded());
-
-        if (!Arrays.areEqual(tsResponse.getEncoded(), tsResp.getEncoded())
-            || !Arrays.areEqual(tsResponse.getTimeStampToken().getEncoded(),
-                        tsResp.getTimeStampToken().getEncoded()))
-        {
-            fail();
-        }
-    }
-    
-    private void testAccuracyZeroCerts(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.MD5, "1.2");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        tsTokenGen.setAccuracySeconds(1);
-        tsTokenGen.setAccuracyMillis(2);
-        tsTokenGen.setAccuracyMicros(3);
-        
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest          request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
-
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        tsToken.validate(cert, "BC");
-        
-        //
-        // check validation
-        //
-        tsResp.validate(request);
-
-        //
-        // check tstInfo
-        //
-        TimeStampTokenInfo tstInfo = tsToken.getTimeStampInfo();
-        
-        //
-        // check accuracy
-        //
-        GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy();
-        
-        assertEquals(1, accuracy.getSeconds());
-        assertEquals(2, accuracy.getMillis());
-        assertEquals(3, accuracy.getMicros());
-        
-        assertEquals(new BigInteger("23"), tstInfo.getSerialNumber());
-        
-        assertEquals("1.2", tstInfo.getPolicy().getId());
-        
-        //
-        // test certReq
-        //
-        CertStore store = tsToken.getCertificatesAndCRLs("Collection", "BC");
-        
-        Collection certificates = store.getCertificates(null);
-        
-        assertEquals(0, certificates.size());
-    }
-    
-    private void testAccuracyWithCertsAndOrdering(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.MD5, "1.2.3");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-
-        tsTokenGen.setAccuracySeconds(3);
-        tsTokenGen.setAccuracyMillis(1);
-        tsTokenGen.setAccuracyMicros(2);
-        
-        tsTokenGen.setOrdering(true);
-        
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        
-        reqGen.setCertReq(true);
-        
-        TimeStampRequest          request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20], BigInteger.valueOf(100));
-
-        assertTrue(request.getCertReq());
-        
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("23"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        tsToken.validate(cert, "BC");
-        
-        //
-        // check validation
-        //
-        tsResp.validate(request);
-
-        //
-        // check tstInfo
-        //
-        TimeStampTokenInfo tstInfo = tsToken.getTimeStampInfo();
-        
-        //
-        // check accuracy
-        //
-        GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy();
-        
-        assertEquals(3, accuracy.getSeconds());
-        assertEquals(1, accuracy.getMillis());
-        assertEquals(2, accuracy.getMicros());
-        
-        assertEquals(new BigInteger("23"), tstInfo.getSerialNumber());
-        
-        assertEquals("1.2.3", tstInfo.getPolicy().getId());
-        
-        assertEquals(true, tstInfo.isOrdered());
-        
-        assertEquals(tstInfo.getNonce(), BigInteger.valueOf(100));
-        
-        //
-        // test certReq
-        //
-        CertStore store = tsToken.getCertificatesAndCRLs("Collection", "BC");
-        
-        Collection certificates = store.getCertificates(null);
-        
-        assertEquals(2, certificates.size());
-    }   
-    
-    private void testNoNonse(
-        PrivateKey      privateKey,
-        X509Certificate cert,
-        CertStore       certs)
-        throws Exception
-    {
-        TimeStampTokenGenerator tsTokenGen = new TimeStampTokenGenerator(
-                privateKey, cert, TSPAlgorithms.MD5, "1.2.3");
-        
-        tsTokenGen.setCertificatesAndCRLs(certs);
-        
-        TimeStampRequestGenerator reqGen = new TimeStampRequestGenerator();
-        TimeStampRequest          request = reqGen.generate(TSPAlgorithms.SHA1, new byte[20]);
-
-        assertFalse(request.getCertReq());
-        
-        TimeStampResponseGenerator tsRespGen = new TimeStampResponseGenerator(tsTokenGen, TSPAlgorithms.ALLOWED);
-
-        TimeStampResponse tsResp = tsRespGen.generate(request, new BigInteger("24"), new Date(), "BC");
-
-        tsResp = new TimeStampResponse(tsResp.getEncoded());
-
-        TimeStampToken  tsToken = tsResp.getTimeStampToken();
-
-        tsToken.validate(cert, "BC");
-        
-        //
-        // check validation
-        //
-        tsResp.validate(request);
-
-        //
-        // check tstInfo
-        //
-        TimeStampTokenInfo tstInfo = tsToken.getTimeStampInfo();
-        
-        //
-        // check accuracy
-        //
-        GenTimeAccuracy accuracy = tstInfo.getGenTimeAccuracy();
-        
-        assertNull(accuracy);
-        
-        assertEquals(new BigInteger("24"), tstInfo.getSerialNumber());
-        
-        assertEquals("1.2.3", tstInfo.getPolicy().getId());
-        
-        assertEquals(false, tstInfo.isOrdered());
-        
-        assertNull(tstInfo.getNonce());
-        
-        //
-        // test certReq
-        //
-        CertStore store = tsToken.getCertificatesAndCRLs("Collection", "BC");
-        
-        Collection certificates = store.getCertificates(null);
-        
-        assertEquals(0, certificates.size());
-    } 
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/LICENSE.java b/bcprov/src/main/java/org/bouncycastle/LICENSE.java
index b97d88b..b02dfbc 100644
--- a/bcprov/src/main/java/org/bouncycastle/LICENSE.java
+++ b/bcprov/src/main/java/org/bouncycastle/LICENSE.java
@@ -3,7 +3,7 @@
 /**
  * The Bouncy Castle License
  *
- * Copyright (c) 2000-2012 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
+ * Copyright (c) 2000-2013 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
  * <p>
  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software 
  * and associated documentation files (the "Software"), to deal in the Software without restriction, 
@@ -24,7 +24,7 @@
 public class LICENSE
 {
     public static String licenseText =
-      "Copyright (c) 2000-2012 The Legion Of The Bouncy Castle (http://www.bouncycastle.org) "
+      "Copyright (c) 2000-2013 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) "
       + System.getProperty("line.separator")
       + System.getProperty("line.separator")
       + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software "
diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/BZip2Constants.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/BZip2Constants.java
new file mode 100644
index 0000000..e86bdee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/BZip2Constants.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.bouncycastle.apache.bzip2;
+
+/**
+ * Base class for both the compress and decompress classes.
+ * Holds common arrays, and static data.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ */
+public interface BZip2Constants {
+
+    int baseBlockSize = 100000;
+    int MAX_ALPHA_SIZE = 258;
+    int MAX_CODE_LEN = 23;
+    int RUNA = 0;
+    int RUNB = 1;
+    int N_GROUPS = 6;
+    int G_SIZE = 50;
+    int N_ITERS = 4;
+    int MAX_SELECTORS = (2 + (900000 / G_SIZE));
+    int NUM_OVERSHOOT_BYTES = 20;
+
+    int[] rNums = {
+        619, 720, 127, 481, 931, 816, 813, 233, 566, 247,
+        985, 724, 205, 454, 863, 491, 741, 242, 949, 214,
+        733, 859, 335, 708, 621, 574, 73, 654, 730, 472,
+        419, 436, 278, 496, 867, 210, 399, 680, 480, 51,
+        878, 465, 811, 169, 869, 675, 611, 697, 867, 561,
+        862, 687, 507, 283, 482, 129, 807, 591, 733, 623,
+        150, 238, 59, 379, 684, 877, 625, 169, 643, 105,
+        170, 607, 520, 932, 727, 476, 693, 425, 174, 647,
+        73, 122, 335, 530, 442, 853, 695, 249, 445, 515,
+        909, 545, 703, 919, 874, 474, 882, 500, 594, 612,
+        641, 801, 220, 162, 819, 984, 589, 513, 495, 799,
+        161, 604, 958, 533, 221, 400, 386, 867, 600, 782,
+        382, 596, 414, 171, 516, 375, 682, 485, 911, 276,
+        98, 553, 163, 354, 666, 933, 424, 341, 533, 870,
+        227, 730, 475, 186, 263, 647, 537, 686, 600, 224,
+        469, 68, 770, 919, 190, 373, 294, 822, 808, 206,
+        184, 943, 795, 384, 383, 461, 404, 758, 839, 887,
+        715, 67, 618, 276, 204, 918, 873, 777, 604, 560,
+        951, 160, 578, 722, 79, 804, 96, 409, 713, 940,
+        652, 934, 970, 447, 318, 353, 859, 672, 112, 785,
+        645, 863, 803, 350, 139, 93, 354, 99, 820, 908,
+        609, 772, 154, 274, 580, 184, 79, 626, 630, 742,
+        653, 282, 762, 623, 680, 81, 927, 626, 789, 125,
+        411, 521, 938, 300, 821, 78, 343, 175, 128, 250,
+        170, 774, 972, 275, 999, 639, 495, 78, 352, 126,
+        857, 956, 358, 619, 580, 124, 737, 594, 701, 612,
+        669, 112, 134, 694, 363, 992, 809, 743, 168, 974,
+        944, 375, 748, 52, 600, 747, 642, 182, 862, 81,
+        344, 805, 988, 739, 511, 655, 814, 334, 249, 515,
+        897, 955, 664, 981, 649, 113, 974, 459, 893, 228,
+        433, 837, 553, 268, 926, 240, 102, 654, 459, 51,
+        686, 754, 806, 760, 493, 403, 415, 394, 687, 700,
+        946, 670, 656, 610, 738, 392, 760, 799, 887, 653,
+        978, 321, 576, 617, 626, 502, 894, 679, 243, 440,
+        680, 879, 194, 572, 640, 724, 926, 56, 204, 700,
+        707, 151, 457, 449, 797, 195, 791, 558, 945, 679,
+        297, 59, 87, 824, 713, 663, 412, 693, 342, 606,
+        134, 108, 571, 364, 631, 212, 174, 643, 304, 329,
+        343, 97, 430, 751, 497, 314, 983, 374, 822, 928,
+        140, 206, 73, 263, 980, 736, 876, 478, 430, 305,
+        170, 514, 364, 692, 829, 82, 855, 953, 676, 246,
+        369, 970, 294, 750, 807, 827, 150, 790, 288, 923,
+        804, 378, 215, 828, 592, 281, 565, 555, 710, 82,
+        896, 831, 547, 261, 524, 462, 293, 465, 502, 56,
+        661, 821, 976, 991, 658, 869, 905, 758, 745, 193,
+        768, 550, 608, 933, 378, 286, 215, 979, 792, 961,
+        61, 688, 793, 644, 986, 403, 106, 366, 905, 644,
+        372, 567, 466, 434, 645, 210, 389, 550, 919, 135,
+        780, 773, 635, 389, 707, 100, 626, 958, 165, 504,
+        920, 176, 193, 713, 857, 265, 203, 50, 668, 108,
+        645, 990, 626, 197, 510, 357, 358, 850, 858, 364,
+        936, 638
+    };
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2InputStream.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2InputStream.java
new file mode 100644
index 0000000..08d05e7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2InputStream.java
@@ -0,0 +1,848 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+package org.bouncycastle.apache.bzip2;
+
+import java.io.InputStream;
+import java.io.IOException;
+
+/**
+ * An input stream that decompresses from the BZip2 format (with the file
+ * header chars) to be read as any other stream.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ *
+ * <b>NB:</b> note this class has been modified to read the leading BZ from the
+ * start of the BZIP2 stream to make it compatible with other PGP programs.
+ */
+public class CBZip2InputStream extends InputStream implements BZip2Constants {
+    private static void cadvise() {
+        System.out.println("CRC Error");
+        //throw new CCoruptionError();
+    }
+
+//    private static void badBGLengths() {
+//        cadvise();
+//    }
+//
+//    private static void bitStreamEOF() {
+//        cadvise();
+//    }
+
+    private static void compressedStreamEOF() {
+        cadvise();
+    }
+
+    private void makeMaps() {
+        int i;
+        nInUse = 0;
+        for (i = 0; i < 256; i++) {
+            if (inUse[i]) {
+                seqToUnseq[nInUse] = (char) i;
+                unseqToSeq[i] = (char) nInUse;
+                nInUse++;
+            }
+        }
+    }
+
+    /*
+      index of the last char in the block, so
+      the block size == last + 1.
+    */
+    private int  last;
+
+    /*
+      index in zptr[] of original string after sorting.
+    */
+    private int  origPtr;
+
+    /*
+      always: in the range 0 .. 9.
+      The current block size is 100000 * this number.
+    */
+    private int blockSize100k;
+
+    private boolean blockRandomised;
+
+    private int bsBuff;
+    private int bsLive;
+    private CRC mCrc = new CRC();
+
+    private boolean[] inUse = new boolean[256];
+    private int nInUse;
+
+    private char[] seqToUnseq = new char[256];
+    private char[] unseqToSeq = new char[256];
+
+    private char[] selector = new char[MAX_SELECTORS];
+    private char[] selectorMtf = new char[MAX_SELECTORS];
+
+    private int[] tt;
+    private char[] ll8;
+
+    /*
+      freq table collected to save a pass over the data
+      during decompression.
+    */
+    private int[] unzftab = new int[256];
+
+    private int[][] limit = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[][] base = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[][] perm = new int[N_GROUPS][MAX_ALPHA_SIZE];
+    private int[] minLens = new int[N_GROUPS];
+
+    private InputStream bsStream;
+
+    private boolean streamEnd = false;
+
+    private int currentChar = -1;
+
+    private static final int START_BLOCK_STATE = 1;
+    private static final int RAND_PART_A_STATE = 2;
+    private static final int RAND_PART_B_STATE = 3;
+    private static final int RAND_PART_C_STATE = 4;
+    private static final int NO_RAND_PART_A_STATE = 5;
+    private static final int NO_RAND_PART_B_STATE = 6;
+    private static final int NO_RAND_PART_C_STATE = 7;
+
+    private int currentState = START_BLOCK_STATE;
+
+    private int storedBlockCRC, storedCombinedCRC;
+    private int computedBlockCRC, computedCombinedCRC;
+
+    int i2, count, chPrev, ch2;
+    int i, tPos;
+    int rNToGo = 0;
+    int rTPos  = 0;
+    int j2;
+    char z;
+
+    public CBZip2InputStream(InputStream zStream)
+        throws IOException
+    {
+        ll8 = null;
+        tt = null;
+        bsSetStream(zStream);
+        initialize();
+        initBlock();
+        setupBlock();
+    }
+
+    public int read() {
+        if (streamEnd) {
+            return -1;
+        } else {
+            int retChar = currentChar;
+            switch(currentState) {
+            case START_BLOCK_STATE:
+                break;
+            case RAND_PART_A_STATE:
+                break;
+            case RAND_PART_B_STATE:
+                setupRandPartB();
+                break;
+            case RAND_PART_C_STATE:
+                setupRandPartC();
+                break;
+            case NO_RAND_PART_A_STATE:
+                break;
+            case NO_RAND_PART_B_STATE:
+                setupNoRandPartB();
+                break;
+            case NO_RAND_PART_C_STATE:
+                setupNoRandPartC();
+                break;
+            default:
+                break;
+            }
+            return retChar;
+        }
+    }
+
+    private void initialize() throws IOException {
+        char magic3, magic4;
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        if (magic3 != 'B' && magic4 != 'Z')
+        {
+            throw new IOException("Not a BZIP2 marked stream");
+        }
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        if (magic3 != 'h' || magic4 < '1' || magic4 > '9') {
+            bsFinishedWithStream();
+            streamEnd = true;
+            return;
+        }
+
+        setDecompressStructureSizes(magic4 - '0');
+        computedCombinedCRC = 0;
+    }
+
+    private void initBlock() {
+        char magic1, magic2, magic3, magic4;
+        char magic5, magic6;
+        magic1 = bsGetUChar();
+        magic2 = bsGetUChar();
+        magic3 = bsGetUChar();
+        magic4 = bsGetUChar();
+        magic5 = bsGetUChar();
+        magic6 = bsGetUChar();
+        if (magic1 == 0x17 && magic2 == 0x72 && magic3 == 0x45
+            && magic4 == 0x38 && magic5 == 0x50 && magic6 == 0x90) {
+            complete();
+            return;
+        }
+
+        if (magic1 != 0x31 || magic2 != 0x41 || magic3 != 0x59
+            || magic4 != 0x26 || magic5 != 0x53 || magic6 != 0x59) {
+            badBlockHeader();
+            streamEnd = true;
+            return;
+        }
+
+        storedBlockCRC = bsGetInt32();
+
+        if (bsR(1) == 1) {
+            blockRandomised = true;
+        } else {
+            blockRandomised = false;
+        }
+
+        //        currBlockNo++;
+        getAndMoveToFrontDecode();
+
+        mCrc.initialiseCRC();
+        currentState = START_BLOCK_STATE;
+    }
+
+    private void endBlock() {
+        computedBlockCRC = mCrc.getFinalCRC();
+        /* A bad CRC is considered a fatal error. */
+        if (storedBlockCRC != computedBlockCRC) {
+            crcError();
+        }
+
+        computedCombinedCRC = (computedCombinedCRC << 1)
+            | (computedCombinedCRC >>> 31);
+        computedCombinedCRC ^= computedBlockCRC;
+    }
+
+    private void complete() {
+        storedCombinedCRC = bsGetInt32();
+        if (storedCombinedCRC != computedCombinedCRC) {
+            crcError();
+        }
+
+        bsFinishedWithStream();
+        streamEnd = true;
+    }
+
+    private static void blockOverrun() {
+        cadvise();
+    }
+
+    private static void badBlockHeader() {
+        cadvise();
+    }
+
+    private static void crcError() {
+        cadvise();
+    }
+
+    private void bsFinishedWithStream() {
+        try {
+            if (this.bsStream != null) {
+                if (this.bsStream != System.in) {
+                    this.bsStream.close();
+                    this.bsStream = null;
+                }
+            }
+        } catch (IOException ioe) {
+            //ignore
+        }
+    }
+
+    private void bsSetStream(InputStream f) {
+        bsStream = f;
+        bsLive = 0;
+        bsBuff = 0;
+    }
+
+    private int bsR(int n) {
+        int v;
+        while (bsLive < n) {
+            int zzi;
+            char thech = 0;
+            try {
+                thech = (char) bsStream.read();
+            } catch (IOException e) {
+                compressedStreamEOF();
+            }
+            if (thech == -1) {
+                compressedStreamEOF();
+            }
+            zzi = thech;
+            bsBuff = (bsBuff << 8) | (zzi & 0xff);
+            bsLive += 8;
+        }
+
+        v = (bsBuff >> (bsLive - n)) & ((1 << n) - 1);
+        bsLive -= n;
+        return v;
+    }
+
+    private char bsGetUChar() {
+        return (char) bsR(8);
+    }
+
+    private int bsGetint() {
+        int u = 0;
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        u = (u << 8) | bsR(8);
+        return u;
+    }
+
+    private int bsGetIntVS(int numBits) {
+        return (int) bsR(numBits);
+    }
+
+    private int bsGetInt32() {
+        return (int) bsGetint();
+    }
+
+    private void hbCreateDecodeTables(int[] limit, int[] base,
+                                      int[] perm, char[] length,
+                                      int minLen, int maxLen, int alphaSize) {
+        int pp, i, j, vec;
+
+        pp = 0;
+        for (i = minLen; i <= maxLen; i++) {
+            for (j = 0; j < alphaSize; j++) {
+                if (length[j] == i) {
+                    perm[pp] = j;
+                    pp++;
+                }
+            }
+        }
+
+        for (i = 0; i < MAX_CODE_LEN; i++) {
+            base[i] = 0;
+        }
+        for (i = 0; i < alphaSize; i++) {
+            base[length[i] + 1]++;
+        }
+
+        for (i = 1; i < MAX_CODE_LEN; i++) {
+            base[i] += base[i - 1];
+        }
+
+        for (i = 0; i < MAX_CODE_LEN; i++) {
+            limit[i] = 0;
+        }
+        vec = 0;
+
+        for (i = minLen; i <= maxLen; i++) {
+            vec += (base[i + 1] - base[i]);
+            limit[i] = vec - 1;
+            vec <<= 1;
+        }
+        for (i = minLen + 1; i <= maxLen; i++) {
+            base[i] = ((limit[i - 1] + 1) << 1) - base[i];
+        }
+    }
+
+    private void recvDecodingTables() {
+        char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE];
+        int i, j, t, nGroups, nSelectors, alphaSize;
+        int minLen, maxLen;
+        boolean[] inUse16 = new boolean[16];
+
+        /* Receive the mapping table */
+        for (i = 0; i < 16; i++) {
+            if (bsR(1) == 1) {
+                inUse16[i] = true;
+            } else {
+                inUse16[i] = false;
+            }
+        }
+
+        for (i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        for (i = 0; i < 16; i++) {
+            if (inUse16[i]) {
+                for (j = 0; j < 16; j++) {
+                    if (bsR(1) == 1) {
+                        inUse[i * 16 + j] = true;
+                    }
+                }
+            }
+        }
+
+        makeMaps();
+        alphaSize = nInUse + 2;
+
+        /* Now the selectors */
+        nGroups = bsR(3);
+        nSelectors = bsR(15);
+        for (i = 0; i < nSelectors; i++) {
+            j = 0;
+            while (bsR(1) == 1) {
+                j++;
+            }
+            selectorMtf[i] = (char) j;
+        }
+
+        /* Undo the MTF values for the selectors. */
+        {
+            char[] pos = new char[N_GROUPS];
+            char tmp, v;
+            for (v = 0; v < nGroups; v++) {
+                pos[v] = v;
+            }
+
+            for (i = 0; i < nSelectors; i++) {
+                v = selectorMtf[i];
+                tmp = pos[v];
+                while (v > 0) {
+                    pos[v] = pos[v - 1];
+                    v--;
+                }
+                pos[0] = tmp;
+                selector[i] = tmp;
+            }
+        }
+
+        /* Now the coding tables */
+        for (t = 0; t < nGroups; t++) {
+            int curr = bsR(5);
+            for (i = 0; i < alphaSize; i++) {
+                while (bsR(1) == 1) {
+                    if (bsR(1) == 0) {
+                        curr++;
+                    } else {
+                        curr--;
+                    }
+                }
+                len[t][i] = (char) curr;
+            }
+        }
+
+        /* Create the Huffman decoding tables */
+        for (t = 0; t < nGroups; t++) {
+            minLen = 32;
+            maxLen = 0;
+            for (i = 0; i < alphaSize; i++) {
+                if (len[t][i] > maxLen) {
+                    maxLen = len[t][i];
+                }
+                if (len[t][i] < minLen) {
+                    minLen = len[t][i];
+                }
+            }
+            hbCreateDecodeTables(limit[t], base[t], perm[t], len[t], minLen,
+                                 maxLen, alphaSize);
+            minLens[t] = minLen;
+        }
+    }
+
+    private void getAndMoveToFrontDecode() {
+        char[] yy = new char[256];
+        int i, j, nextSym, limitLast;
+        int EOB, groupNo, groupPos;
+
+        limitLast = baseBlockSize * blockSize100k;
+        origPtr = bsGetIntVS(24);
+
+        recvDecodingTables();
+        EOB = nInUse + 1;
+        groupNo = -1;
+        groupPos = 0;
+
+        /*
+          Setting up the unzftab entries here is not strictly
+          necessary, but it does save having to do it later
+          in a separate pass, and so saves a block's worth of
+          cache misses.
+        */
+        for (i = 0; i <= 255; i++) {
+            unzftab[i] = 0;
+        }
+
+        for (i = 0; i <= 255; i++) {
+            yy[i] = (char) i;
+        }
+
+        last = -1;
+
+        {
+            int zt, zn, zvec, zj;
+            if (groupPos == 0) {
+                groupNo++;
+                groupPos = G_SIZE;
+            }
+            groupPos--;
+            zt = selector[groupNo];
+            zn = minLens[zt];
+            zvec = bsR(zn);
+            while (zvec > limit[zt][zn]) {
+                zn++;
+                {
+                    {
+                        while (bsLive < 1) {
+                            int zzi;
+                            char thech = 0;
+                            try {
+                                thech = (char) bsStream.read();
+                            } catch (IOException e) {
+                                compressedStreamEOF();
+                            }
+                            if (thech == -1) {
+                                compressedStreamEOF();
+                            }
+                            zzi = thech;
+                            bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                            bsLive += 8;
+                        }
+                    }
+                    zj = (bsBuff >> (bsLive - 1)) & 1;
+                    bsLive--;
+                }
+                zvec = (zvec << 1) | zj;
+            }
+            nextSym = perm[zt][zvec - base[zt][zn]];
+        }
+
+        while (true) {
+
+            if (nextSym == EOB) {
+                break;
+            }
+
+            if (nextSym == RUNA || nextSym == RUNB) {
+                char ch;
+                int s = -1;
+                int N = 1;
+                do {
+                    if (nextSym == RUNA) {
+                        s = s + (0 + 1) * N;
+                    } else if (nextSym == RUNB) {
+                        s = s + (1 + 1) * N;
+                           }
+                    N = N * 2;
+                    {
+                        int zt, zn, zvec, zj;
+                        if (groupPos == 0) {
+                            groupNo++;
+                            groupPos = G_SIZE;
+                        }
+                        groupPos--;
+                        zt = selector[groupNo];
+                        zn = minLens[zt];
+                        zvec = bsR(zn);
+                        while (zvec > limit[zt][zn]) {
+                            zn++;
+                            {
+                                {
+                                    while (bsLive < 1) {
+                                        int zzi;
+                                        char thech = 0;
+                                        try {
+                                            thech = (char) bsStream.read();
+                                        } catch (IOException e) {
+                                            compressedStreamEOF();
+                                        }
+                                        if (thech == -1) {
+                                            compressedStreamEOF();
+                                        }
+                                        zzi = thech;
+                                        bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                                        bsLive += 8;
+                                    }
+                                }
+                                zj = (bsBuff >> (bsLive - 1)) & 1;
+                                bsLive--;
+                            }
+                            zvec = (zvec << 1) | zj;
+                        }
+                        nextSym = perm[zt][zvec - base[zt][zn]];
+                    }
+                } while (nextSym == RUNA || nextSym == RUNB);
+
+                s++;
+                ch = seqToUnseq[yy[0]];
+                unzftab[ch] += s;
+
+                while (s > 0) {
+                    last++;
+                    ll8[last] = ch;
+                    s--;
+                }
+
+                if (last >= limitLast) {
+                    blockOverrun();
+                }
+                continue;
+            } else {
+                char tmp;
+                last++;
+                if (last >= limitLast) {
+                    blockOverrun();
+                }
+
+                tmp = yy[nextSym - 1];
+                unzftab[seqToUnseq[tmp]]++;
+                ll8[last] = seqToUnseq[tmp];
+
+                /*
+                  This loop is hammered during decompression,
+                  hence the unrolling.
+
+                  for (j = nextSym-1; j > 0; j--) yy[j] = yy[j-1];
+                */
+
+                j = nextSym - 1;
+                for (; j > 3; j -= 4) {
+                    yy[j]     = yy[j - 1];
+                    yy[j - 1] = yy[j - 2];
+                    yy[j - 2] = yy[j - 3];
+                    yy[j - 3] = yy[j - 4];
+                }
+                for (; j > 0; j--) {
+                    yy[j] = yy[j - 1];
+                }
+
+                yy[0] = tmp;
+                {
+                    int zt, zn, zvec, zj;
+                    if (groupPos == 0) {
+                        groupNo++;
+                        groupPos = G_SIZE;
+                    }
+                    groupPos--;
+                    zt = selector[groupNo];
+                    zn = minLens[zt];
+                    zvec = bsR(zn);
+                    while (zvec > limit[zt][zn]) {
+                        zn++;
+                        {
+                            {
+                                while (bsLive < 1) {
+                                    int zzi;
+                                    char thech = 0;
+                                    try {
+                                        thech = (char) bsStream.read();
+                                    } catch (IOException e) {
+                                        compressedStreamEOF();
+                                    }
+                                    zzi = thech;
+                                    bsBuff = (bsBuff << 8) | (zzi & 0xff);
+                                    bsLive += 8;
+                                }
+                            }
+                            zj = (bsBuff >> (bsLive - 1)) & 1;
+                            bsLive--;
+                        }
+                        zvec = (zvec << 1) | zj;
+                    }
+                    nextSym = perm[zt][zvec - base[zt][zn]];
+                }
+                continue;
+            }
+        }
+    }
+
+    private void setupBlock() {
+        int[] cftab = new int[257];
+        char ch;
+
+        cftab[0] = 0;
+        for (i = 1; i <= 256; i++) {
+            cftab[i] = unzftab[i - 1];
+        }
+        for (i = 1; i <= 256; i++) {
+            cftab[i] += cftab[i - 1];
+        }
+
+        for (i = 0; i <= last; i++) {
+            ch = (char) ll8[i];
+            tt[cftab[ch]] = i;
+            cftab[ch]++;
+        }
+        cftab = null;
+
+        tPos = tt[origPtr];
+
+        count = 0;
+        i2 = 0;
+        ch2 = 256;   /* not a char and not EOF */
+
+        if (blockRandomised) {
+            rNToGo = 0;
+            rTPos = 0;
+            setupRandPartA();
+        } else {
+            setupNoRandPartA();
+        }
+    }
+
+    private void setupRandPartA() {
+        if (i2 <= last) {
+            chPrev = ch2;
+            ch2 = ll8[tPos];
+            tPos = tt[tPos];
+            if (rNToGo == 0) {
+                rNToGo = rNums[rTPos];
+                rTPos++;
+                if (rTPos == 512) {
+                    rTPos = 0;
+                }
+            }
+            rNToGo--;
+            ch2 ^= (int) ((rNToGo == 1) ? 1 : 0);
+            i2++;
+
+            currentChar = ch2;
+            currentState = RAND_PART_B_STATE;
+            mCrc.updateCRC(ch2);
+        } else {
+            endBlock();
+            initBlock();
+            setupBlock();
+        }
+    }
+
+    private void setupNoRandPartA() {
+        if (i2 <= last) {
+            chPrev = ch2;
+            ch2 = ll8[tPos];
+            tPos = tt[tPos];
+            i2++;
+
+            currentChar = ch2;
+            currentState = NO_RAND_PART_B_STATE;
+            mCrc.updateCRC(ch2);
+        } else {
+            endBlock();
+            initBlock();
+            setupBlock();
+        }
+    }
+
+    private void setupRandPartB() {
+        if (ch2 != chPrev) {
+            currentState = RAND_PART_A_STATE;
+            count = 1;
+            setupRandPartA();
+        } else {
+            count++;
+            if (count >= 4) {
+                z = ll8[tPos];
+                tPos = tt[tPos];
+                if (rNToGo == 0) {
+                    rNToGo = rNums[rTPos];
+                    rTPos++;
+                    if (rTPos == 512) {
+                        rTPos = 0;
+                    }
+                }
+                rNToGo--;
+                z ^= ((rNToGo == 1) ? 1 : 0);
+                j2 = 0;
+                currentState = RAND_PART_C_STATE;
+                setupRandPartC();
+            } else {
+                currentState = RAND_PART_A_STATE;
+                setupRandPartA();
+            }
+        }
+    }
+
+    private void setupRandPartC() {
+        if (j2 < (int) z) {
+            currentChar = ch2;
+            mCrc.updateCRC(ch2);
+            j2++;
+        } else {
+            currentState = RAND_PART_A_STATE;
+            i2++;
+            count = 0;
+            setupRandPartA();
+        }
+    }
+
+    private void setupNoRandPartB() {
+        if (ch2 != chPrev) {
+            currentState = NO_RAND_PART_A_STATE;
+            count = 1;
+            setupNoRandPartA();
+        } else {
+            count++;
+            if (count >= 4) {
+                z = ll8[tPos];
+                tPos = tt[tPos];
+                currentState = NO_RAND_PART_C_STATE;
+                j2 = 0;
+                setupNoRandPartC();
+            } else {
+                currentState = NO_RAND_PART_A_STATE;
+                setupNoRandPartA();
+            }
+        }
+    }
+
+    private void setupNoRandPartC() {
+        if (j2 < (int) z) {
+            currentChar = ch2;
+            mCrc.updateCRC(ch2);
+            j2++;
+        } else {
+            currentState = NO_RAND_PART_A_STATE;
+            i2++;
+            count = 0;
+            setupNoRandPartA();
+        }
+    }
+
+    private void setDecompressStructureSizes(int newSize100k) {
+        if (!(0 <= newSize100k && newSize100k <= 9 && 0 <= blockSize100k
+               && blockSize100k <= 9)) {
+            // throw new IOException("Invalid block size");
+        }
+
+        blockSize100k = newSize100k;
+
+        if (newSize100k == 0) {
+            return;
+        }
+
+        int n = baseBlockSize * newSize100k;
+        ll8 = new char[n];
+        tt = new int[n];
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2OutputStream.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2OutputStream.java
new file mode 100644
index 0000000..0503583
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CBZip2OutputStream.java
@@ -0,0 +1,1651 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.bouncycastle.apache.bzip2;
+
+import java.io.OutputStream;
+import java.io.IOException;
+
+/**
+ * An output stream that compresses into the BZip2 format (with the file
+ * header chars) into another stream.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ *
+ * TODO:    Update to BZip2 1.0.1
+ * <b>NB:</b> note this class has been modified to add a leading BZ to the
+ * start of the BZIP2 stream to make it compatible with other PGP programs.
+ */
+public class CBZip2OutputStream extends OutputStream implements BZip2Constants {
+    protected static final int SETMASK = (1 << 21);
+    protected static final int CLEARMASK = (~SETMASK);
+    protected static final int GREATER_ICOST = 15;
+    protected static final int LESSER_ICOST = 0;
+    protected static final int SMALL_THRESH = 20;
+    protected static final int DEPTH_THRESH = 10;
+
+    /*
+      If you are ever unlucky/improbable enough
+      to get a stack overflow whilst sorting,
+      increase the following constant and try
+      again.  In practice I have never seen the
+      stack go above 27 elems, so the following
+      limit seems very generous.
+    */
+    protected static final int QSORT_STACK_SIZE = 1000;
+    private boolean finished;
+
+    private static void panic() {
+        System.out.println("panic");
+        //throw new CError();
+    }
+
+    private void makeMaps() {
+        int i;
+        nInUse = 0;
+        for (i = 0; i < 256; i++) {
+            if (inUse[i]) {
+                seqToUnseq[nInUse] = (char) i;
+                unseqToSeq[i] = (char) nInUse;
+                nInUse++;
+            }
+        }
+    }
+
+    protected static void hbMakeCodeLengths(char[] len, int[] freq,
+                                            int alphaSize, int maxLen) {
+        /*
+          Nodes and heap entries run from 1.  Entry 0
+          for both the heap and nodes is a sentinel.
+        */
+        int nNodes, nHeap, n1, n2, i, j, k;
+        boolean  tooLong;
+
+        int[] heap = new int[MAX_ALPHA_SIZE + 2];
+        int[] weight = new int[MAX_ALPHA_SIZE * 2];
+        int[] parent = new int[MAX_ALPHA_SIZE * 2];
+
+        for (i = 0; i < alphaSize; i++) {
+            weight[i + 1] = (freq[i] == 0 ? 1 : freq[i]) << 8;
+        }
+
+        while (true) {
+            nNodes = alphaSize;
+            nHeap = 0;
+
+            heap[0] = 0;
+            weight[0] = 0;
+            parent[0] = -2;
+
+            for (i = 1; i <= alphaSize; i++) {
+                parent[i] = -1;
+                nHeap++;
+                heap[nHeap] = i;
+                {
+                    int zz, tmp;
+                    zz = nHeap;
+                    tmp = heap[zz];
+                    while (weight[tmp] < weight[heap[zz >> 1]]) {
+                        heap[zz] = heap[zz >> 1];
+                        zz >>= 1;
+                    }
+                    heap[zz] = tmp;
+                }
+            }
+            if (!(nHeap < (MAX_ALPHA_SIZE + 2))) {
+                panic();
+            }
+
+            while (nHeap > 1) {
+                n1 = heap[1];
+                heap[1] = heap[nHeap];
+                nHeap--;
+                {
+                    int zz = 0, yy = 0, tmp = 0;
+                    zz = 1;
+                    tmp = heap[zz];
+                    while (true) {
+                        yy = zz << 1;
+                        if (yy > nHeap) {
+                            break;
+                        }
+                        if (yy < nHeap
+                            && weight[heap[yy + 1]] < weight[heap[yy]]) {
+                            yy++;
+                        }
+                        if (weight[tmp] < weight[heap[yy]]) {
+                            break;
+                        }
+                        heap[zz] = heap[yy];
+                        zz = yy;
+                    }
+                    heap[zz] = tmp;
+                }
+                n2 = heap[1];
+                heap[1] = heap[nHeap];
+                nHeap--;
+                {
+                    int zz = 0, yy = 0, tmp = 0;
+                    zz = 1;
+                    tmp = heap[zz];
+                    while (true) {
+                        yy = zz << 1;
+                        if (yy > nHeap) {
+                            break;
+                        }
+                        if (yy < nHeap
+                            && weight[heap[yy + 1]] < weight[heap[yy]]) {
+                            yy++;
+                        }
+                        if (weight[tmp] < weight[heap[yy]]) {
+                            break;
+                        }
+                        heap[zz] = heap[yy];
+                        zz = yy;
+                    }
+                    heap[zz] = tmp;
+                }
+                nNodes++;
+                parent[n1] = parent[n2] = nNodes;
+
+                weight[nNodes] = ((weight[n1] & 0xffffff00)
+                                  + (weight[n2] & 0xffffff00))
+                    | (1 + (((weight[n1] & 0x000000ff) >
+                             (weight[n2] & 0x000000ff)) ?
+                            (weight[n1] & 0x000000ff) :
+                            (weight[n2] & 0x000000ff)));
+
+                parent[nNodes] = -1;
+                nHeap++;
+                heap[nHeap] = nNodes;
+                {
+                    int zz = 0, tmp = 0;
+                    zz = nHeap;
+                    tmp = heap[zz];
+                    while (weight[tmp] < weight[heap[zz >> 1]]) {
+                        heap[zz] = heap[zz >> 1];
+                        zz >>= 1;
+                    }
+                    heap[zz] = tmp;
+                }
+            }
+            if (!(nNodes < (MAX_ALPHA_SIZE * 2))) {
+                panic();
+            }
+
+            tooLong = false;
+            for (i = 1; i <= alphaSize; i++) {
+                j = 0;
+                k = i;
+                while (parent[k] >= 0) {
+                    k = parent[k];
+                    j++;
+                }
+                len[i - 1] = (char) j;
+                if (j > maxLen) {
+                    tooLong = true;
+                }
+            }
+
+            if (!tooLong) {
+                break;
+            }
+
+            for (i = 1; i < alphaSize; i++) {
+                j = weight[i] >> 8;
+                j = 1 + (j / 2);
+                weight[i] = j << 8;
+            }
+        }
+    }
+
+    /*
+      index of the last char in the block, so
+      the block size == last + 1.
+    */
+    int last;
+
+    /*
+      index in zptr[] of original string after sorting.
+    */
+    int origPtr;
+
+    /*
+      always: in the range 0 .. 9.
+      The current block size is 100000 * this number.
+    */
+    int blockSize100k;
+
+    boolean blockRandomised;
+
+    int bytesOut;
+    int bsBuff;
+    int bsLive;
+    CRC mCrc = new CRC();
+
+    private boolean[] inUse = new boolean[256];
+    private int nInUse;
+
+    private char[] seqToUnseq = new char[256];
+    private char[] unseqToSeq = new char[256];
+
+    private char[] selector = new char[MAX_SELECTORS];
+    private char[] selectorMtf = new char[MAX_SELECTORS];
+
+    private char[] block;
+    private int[] quadrant;
+    private int[] zptr;
+    private short[] szptr;
+    private int[] ftab;
+
+    private int nMTF;
+
+    private int[] mtfFreq = new int[MAX_ALPHA_SIZE];
+
+    /*
+     * Used when sorting.  If too many long comparisons
+     * happen, we stop sorting, randomise the block
+     * slightly, and try again.
+     */
+    private int workFactor;
+    private int workDone;
+    private int workLimit;
+    private boolean firstAttempt;
+    private int nBlocksRandomised;
+
+    private int currentChar = -1;
+    private int runLength = 0;
+
+    public CBZip2OutputStream(OutputStream inStream) throws IOException {
+        this(inStream, 9);
+    }
+
+    public CBZip2OutputStream(OutputStream inStream, int inBlockSize)
+        throws IOException {
+        block = null;
+        quadrant = null;
+        zptr = null;
+        ftab = null;
+
+        inStream.write('B');
+        inStream.write('Z');
+
+        bsSetStream(inStream);
+
+        workFactor = 50;
+        if (inBlockSize > 9) {
+            inBlockSize = 9;
+        }
+        if (inBlockSize < 1) {
+            inBlockSize = 1;
+        }
+        blockSize100k = inBlockSize;
+        allocateCompressStructures();
+        initialize();
+        initBlock();
+    }
+
+    /**
+     *
+     * modified by Oliver Merkel, 010128
+     *
+     */
+    public void write(int bv) throws IOException {
+        int b = (256 + bv) % 256;
+        if (currentChar != -1) {
+            if (currentChar == b) {
+                runLength++;
+                if (runLength > 254) {
+                    writeRun();
+                    currentChar = -1;
+                    runLength = 0;
+                }
+            } else {
+                writeRun();
+                runLength = 1;
+                currentChar = b;
+            }
+        } else {
+            currentChar = b;
+            runLength++;
+        }
+    }
+
+    private void writeRun() throws IOException {
+        if (last < allowableBlockSize) {
+            inUse[currentChar] = true;
+            for (int i = 0; i < runLength; i++) {
+                mCrc.updateCRC((char) currentChar);
+            }
+            switch (runLength) {
+            case 1:
+                last++;
+                block[last + 1] = (char) currentChar;
+                break;
+            case 2:
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                break;
+            case 3:
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                break;
+            default:
+                inUse[runLength - 4] = true;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) currentChar;
+                last++;
+                block[last + 1] = (char) (runLength - 4);
+                break;
+            }
+        } else {
+            endBlock();
+            initBlock();
+            writeRun();
+        }
+    }
+
+    boolean closed = false;
+
+    protected void finalize() throws Throwable {
+        close();
+        super.finalize();
+    }
+
+    public void close() throws IOException {
+        if (closed) {
+            return;
+        }
+
+        finish();
+
+        closed = true;
+        super.close();
+        bsStream.close();
+    }
+
+    public void finish() throws IOException {
+        if (finished) {
+            return;
+        }
+
+        if (runLength > 0) {
+            writeRun();
+        }
+        currentChar = -1;
+        endBlock();
+        endCompression();
+        finished = true;
+        flush();
+    }
+    
+    public void flush() throws IOException {
+        super.flush();
+        bsStream.flush();
+    }
+
+    private int blockCRC, combinedCRC;
+
+    private void initialize() throws IOException {
+        bytesOut = 0;
+        nBlocksRandomised = 0;
+
+        /* Write `magic' bytes h indicating file-format == huffmanised,
+           followed by a digit indicating blockSize100k.
+        */
+        bsPutUChar('h');
+        bsPutUChar('0' + blockSize100k);
+
+        combinedCRC = 0;
+    }
+
+    private int allowableBlockSize;
+
+    private void initBlock() {
+        //        blockNo++;
+        mCrc.initialiseCRC();
+        last = -1;
+        //        ch = 0;
+
+        for (int i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        /* 20 is just a paranoia constant */
+        allowableBlockSize = baseBlockSize * blockSize100k - 20;
+    }
+
+    private void endBlock() throws IOException {
+        blockCRC = mCrc.getFinalCRC();
+        combinedCRC = (combinedCRC << 1) | (combinedCRC >>> 31);
+        combinedCRC ^= blockCRC;
+
+        /* sort the block and establish posn of original string */
+        doReversibleTransformation();
+
+        /*
+          A 6-byte block header, the value chosen arbitrarily
+          as 0x314159265359 :-).  A 32 bit value does not really
+          give a strong enough guarantee that the value will not
+          appear by chance in the compressed datastream.  Worst-case
+          probability of this event, for a 900k block, is about
+          2.0e-3 for 32 bits, 1.0e-5 for 40 bits and 4.0e-8 for 48 bits.
+          For a compressed file of size 100Gb -- about 100000 blocks --
+          only a 48-bit marker will do.  NB: normal compression/
+          decompression do *not* rely on these statistical properties.
+          They are only important when trying to recover blocks from
+          damaged files.
+        */
+        bsPutUChar(0x31);
+        bsPutUChar(0x41);
+        bsPutUChar(0x59);
+        bsPutUChar(0x26);
+        bsPutUChar(0x53);
+        bsPutUChar(0x59);
+
+        /* Now the block's CRC, so it is in a known place. */
+        bsPutint(blockCRC);
+
+        /* Now a single bit indicating randomisation. */
+        if (blockRandomised) {
+            bsW(1, 1);
+            nBlocksRandomised++;
+        } else {
+            bsW(1, 0);
+        }
+
+        /* Finally, block's contents proper. */
+        moveToFrontCodeAndSend();
+    }
+
+    private void endCompression() throws IOException {
+        /*
+          Now another magic 48-bit number, 0x177245385090, to
+          indicate the end of the last block.  (sqrt(pi), if
+          you want to know.  I did want to use e, but it contains
+          too much repetition -- 27 18 28 18 28 46 -- for me
+          to feel statistically comfortable.  Call me paranoid.)
+        */
+        bsPutUChar(0x17);
+        bsPutUChar(0x72);
+        bsPutUChar(0x45);
+        bsPutUChar(0x38);
+        bsPutUChar(0x50);
+        bsPutUChar(0x90);
+
+        bsPutint(combinedCRC);
+
+        bsFinishedWithStream();
+    }
+
+    private void hbAssignCodes (int[] code, char[] length, int minLen,
+                                int maxLen, int alphaSize) {
+        int n, vec, i;
+
+        vec = 0;
+        for (n = minLen; n <= maxLen; n++) {
+            for (i = 0; i < alphaSize; i++) {
+                if (length[i] == n) {
+                    code[i] = vec;
+                    vec++;
+                }
+            }
+            vec <<= 1;
+        }
+    }
+
+    private void bsSetStream(OutputStream f) {
+        bsStream = f;
+        bsLive = 0;
+        bsBuff = 0;
+        bytesOut = 0;
+    }
+
+    private void bsFinishedWithStream() throws IOException {
+        while (bsLive > 0) {
+            int ch = (bsBuff >> 24);
+            try {
+                bsStream.write(ch); // write 8-bit
+            } catch (IOException e) {
+                throw  e;
+            }
+            bsBuff <<= 8;
+            bsLive -= 8;
+            bytesOut++;
+        }
+    }
+
+    private void bsW(int n, int v) throws IOException {
+        while (bsLive >= 8) {
+            int ch = (bsBuff >> 24);
+            try {
+                bsStream.write(ch); // write 8-bit
+            } catch (IOException e) {
+                throw e;
+            }
+            bsBuff <<= 8;
+            bsLive -= 8;
+            bytesOut++;
+        }
+        bsBuff |= (v << (32 - bsLive - n));
+        bsLive += n;
+    }
+
+    private void bsPutUChar(int c) throws IOException {
+        bsW(8, c);
+    }
+
+    private void bsPutint(int u) throws IOException {
+        bsW(8, (u >> 24) & 0xff);
+        bsW(8, (u >> 16) & 0xff);
+        bsW(8, (u >>  8) & 0xff);
+        bsW(8,  u        & 0xff);
+    }
+
+    private void bsPutIntVS(int numBits, int c) throws IOException {
+        bsW(numBits, c);
+    }
+
+    private void sendMTFValues() throws IOException {
+        char len[][] = new char[N_GROUPS][MAX_ALPHA_SIZE];
+
+        int v, t, i, j, gs, ge, totc, bt, bc, iter;
+        int nSelectors = 0, alphaSize, minLen, maxLen, selCtr;
+        int nGroups;//, nBytes;
+
+        alphaSize = nInUse + 2;
+        for (t = 0; t < N_GROUPS; t++) {
+            for (v = 0; v < alphaSize; v++) {
+                len[t][v] = (char) GREATER_ICOST;
+            }
+        }
+
+        /* Decide how many coding tables to use */
+        if (nMTF <= 0) {
+            panic();
+        }
+
+        if (nMTF < 200) {
+            nGroups = 2;
+        } else if (nMTF < 600) {
+            nGroups = 3;
+        } else if (nMTF < 1200) {
+            nGroups = 4;
+        } else if (nMTF < 2400) {
+            nGroups = 5;
+        } else {
+            nGroups = 6;
+        }
+
+        /* Generate an initial set of coding tables */ {
+            int nPart, remF, tFreq, aFreq;
+
+            nPart = nGroups;
+            remF  = nMTF;
+            gs = 0;
+            while (nPart > 0) {
+                tFreq = remF / nPart;
+                ge = gs - 1;
+                aFreq = 0;
+                while (aFreq < tFreq && ge < alphaSize - 1) {
+                    ge++;
+                    aFreq += mtfFreq[ge];
+                }
+
+                if (ge > gs && nPart != nGroups && nPart != 1
+                    && ((nGroups - nPart) % 2 == 1)) {
+                    aFreq -= mtfFreq[ge];
+                    ge--;
+                }
+
+                for (v = 0; v < alphaSize; v++) {
+                    if (v >= gs && v <= ge) {
+                        len[nPart - 1][v] = (char) LESSER_ICOST;
+                    } else {
+                        len[nPart - 1][v] = (char) GREATER_ICOST;
+                    }
+                }
+
+                nPart--;
+                gs = ge + 1;
+                remF -= aFreq;
+            }
+        }
+
+        int[][] rfreq = new int[N_GROUPS][MAX_ALPHA_SIZE];
+        int[] fave = new int[N_GROUPS];
+        short[] cost = new short[N_GROUPS];
+        /*
+          Iterate up to N_ITERS times to improve the tables.
+        */
+        for (iter = 0; iter < N_ITERS; iter++) {
+            for (t = 0; t < nGroups; t++) {
+                fave[t] = 0;
+            }
+
+            for (t = 0; t < nGroups; t++) {
+                for (v = 0; v < alphaSize; v++) {
+                    rfreq[t][v] = 0;
+                }
+            }
+
+            nSelectors = 0;
+            totc = 0;
+            gs = 0;
+            while (true) {
+
+                /* Set group start & end marks. */
+                if (gs >= nMTF) {
+                    break;
+                }
+                ge = gs + G_SIZE - 1;
+                if (ge >= nMTF) {
+                    ge = nMTF - 1;
+                }
+
+                /*
+                  Calculate the cost of this group as coded
+                  by each of the coding tables.
+                */
+                for (t = 0; t < nGroups; t++) {
+                    cost[t] = 0;
+                }
+
+                if (nGroups == 6) {
+                    short cost0, cost1, cost2, cost3, cost4, cost5;
+                    cost0 = cost1 = cost2 = cost3 = cost4 = cost5 = 0;
+                    for (i = gs; i <= ge; i++) {
+                        short icv = szptr[i];
+                        cost0 += len[0][icv];
+                        cost1 += len[1][icv];
+                        cost2 += len[2][icv];
+                        cost3 += len[3][icv];
+                        cost4 += len[4][icv];
+                        cost5 += len[5][icv];
+                    }
+                    cost[0] = cost0;
+                    cost[1] = cost1;
+                    cost[2] = cost2;
+                    cost[3] = cost3;
+                    cost[4] = cost4;
+                    cost[5] = cost5;
+                } else {
+                    for (i = gs; i <= ge; i++) {
+                        short icv = szptr[i];
+                        for (t = 0; t < nGroups; t++) {
+                            cost[t] += len[t][icv];
+                        }
+                    }
+                }
+
+                /*
+                  Find the coding table which is best for this group,
+                  and record its identity in the selector table.
+                */
+                bc = 999999999;
+                bt = -1;
+                for (t = 0; t < nGroups; t++) {
+                    if (cost[t] < bc) {
+                        bc = cost[t];
+                        bt = t;
+                    }
+                }
+                totc += bc;
+                fave[bt]++;
+                selector[nSelectors] = (char) bt;
+                nSelectors++;
+
+                /*
+                  Increment the symbol frequencies for the selected table.
+                */
+                for (i = gs; i <= ge; i++) {
+                    rfreq[bt][szptr[i]]++;
+                }
+
+                gs = ge + 1;
+            }
+
+            /*
+              Recompute the tables based on the accumulated frequencies.
+            */
+            for (t = 0; t < nGroups; t++) {
+                hbMakeCodeLengths(len[t], rfreq[t], alphaSize, 20);
+            }
+        }
+
+        rfreq = null;
+        fave = null;
+        cost = null;
+
+        if (!(nGroups < 8)) {
+            panic();
+        }
+        if (!(nSelectors < 32768 && nSelectors <= (2 + (900000 / G_SIZE)))) {
+            panic();
+        }
+
+
+        /* Compute MTF values for the selectors. */
+        {
+            char[] pos = new char[N_GROUPS];
+            char ll_i, tmp2, tmp;
+            for (i = 0; i < nGroups; i++) {
+                pos[i] = (char) i;
+            }
+            for (i = 0; i < nSelectors; i++) {
+                ll_i = selector[i];
+                j = 0;
+                tmp = pos[j];
+                while (ll_i != tmp) {
+                    j++;
+                    tmp2 = tmp;
+                    tmp = pos[j];
+                    pos[j] = tmp2;
+                }
+                pos[0] = tmp;
+                selectorMtf[i] = (char) j;
+            }
+        }
+
+        int[][] code = new int[N_GROUPS][MAX_ALPHA_SIZE];
+
+        /* Assign actual codes for the tables. */
+        for (t = 0; t < nGroups; t++) {
+            minLen = 32;
+            maxLen = 0;
+            for (i = 0; i < alphaSize; i++) {
+                if (len[t][i] > maxLen) {
+                    maxLen = len[t][i];
+                }
+                if (len[t][i] < minLen) {
+                    minLen = len[t][i];
+                }
+            }
+            if (maxLen > 20) {
+                panic();
+            }
+            if (minLen < 1) {
+                panic();
+            }
+            hbAssignCodes(code[t], len[t], minLen, maxLen, alphaSize);
+        }
+
+        /* Transmit the mapping table. */
+        {
+            boolean[] inUse16 = new boolean[16];
+            for (i = 0; i < 16; i++) {
+                inUse16[i] = false;
+                for (j = 0; j < 16; j++) {
+                    if (inUse[i * 16 + j]) {
+                        inUse16[i] = true;
+                    }
+                }
+            }
+
+//            nBytes = bytesOut;
+            for (i = 0; i < 16; i++) {
+                if (inUse16[i]) {
+                    bsW(1, 1);
+                } else {
+                    bsW(1, 0);
+                }
+            }
+
+            for (i = 0; i < 16; i++) {
+                if (inUse16[i]) {
+                    for (j = 0; j < 16; j++) {
+                        if (inUse[i * 16 + j]) {
+                            bsW(1, 1);
+                        } else {
+                            bsW(1, 0);
+                        }
+                    }
+                }
+            }
+
+        }
+
+        /* Now the selectors. */
+//        nBytes = bytesOut;
+        bsW (3, nGroups);
+        bsW (15, nSelectors);
+        for (i = 0; i < nSelectors; i++) {
+            for (j = 0; j < selectorMtf[i]; j++) {
+                bsW(1, 1);
+            }
+            bsW(1, 0);
+        }
+
+        /* Now the coding tables. */
+//        nBytes = bytesOut;
+
+        for (t = 0; t < nGroups; t++) {
+            int curr = len[t][0];
+            bsW(5, curr);
+            for (i = 0; i < alphaSize; i++) {
+                while (curr < len[t][i]) {
+                    bsW(2, 2);
+                    curr++; /* 10 */
+                }
+                while (curr > len[t][i]) {
+                    bsW(2, 3);
+                    curr--; /* 11 */
+                }
+                bsW (1, 0);
+            }
+        }
+
+        /* And finally, the block data proper */
+//        nBytes = bytesOut;
+        selCtr = 0;
+        gs = 0;
+        while (true) {
+            if (gs >= nMTF) {
+                break;
+            }
+            ge = gs + G_SIZE - 1;
+            if (ge >= nMTF) {
+                ge = nMTF - 1;
+            }
+            for (i = gs; i <= ge; i++) {
+                bsW(len[selector[selCtr]][szptr[i]],
+                    code[selector[selCtr]][szptr[i]]);
+            }
+
+            gs = ge + 1;
+            selCtr++;
+        }
+        if (!(selCtr == nSelectors)) {
+            panic();
+        }
+    }
+
+    private void moveToFrontCodeAndSend () throws IOException {
+        bsPutIntVS(24, origPtr);
+        generateMTFValues();
+        sendMTFValues();
+    }
+
+    private OutputStream bsStream;
+
+    private void simpleSort(int lo, int hi, int d) {
+        int i, j, h, bigN, hp;
+        int v;
+
+        bigN = hi - lo + 1;
+        if (bigN < 2) {
+            return;
+        }
+
+        hp = 0;
+        while (incs[hp] < bigN) {
+            hp++;
+        }
+        hp--;
+
+        for (; hp >= 0; hp--) {
+            h = incs[hp];
+
+            i = lo + h;
+            while (true) {
+                /* copy 1 */
+                if (i > hi) {
+                    break;
+                }
+                v = zptr[i];
+                j = i;
+                while (fullGtU(zptr[j - h] + d, v + d)) {
+                    zptr[j] = zptr[j - h];
+                    j = j - h;
+                    if (j <= (lo + h - 1)) {
+                        break;
+                    }
+                }
+                zptr[j] = v;
+                i++;
+
+                /* copy 2 */
+                if (i > hi) {
+                    break;
+                }
+                v = zptr[i];
+                j = i;
+                while (fullGtU(zptr[j - h] + d, v + d)) {
+                    zptr[j] = zptr[j - h];
+                    j = j - h;
+                    if (j <= (lo + h - 1)) {
+                        break;
+                    }
+                }
+                zptr[j] = v;
+                i++;
+
+                /* copy 3 */
+                if (i > hi) {
+                    break;
+                }
+                v = zptr[i];
+                j = i;
+                while (fullGtU(zptr[j - h] + d, v + d)) {
+                    zptr[j] = zptr[j - h];
+                    j = j - h;
+                    if (j <= (lo + h - 1)) {
+                        break;
+                    }
+                }
+                zptr[j] = v;
+                i++;
+
+                if (workDone > workLimit && firstAttempt) {
+                    return;
+                }
+            }
+        }
+    }
+
+    private void vswap(int p1, int p2, int n) {
+        int temp = 0;
+        while (n > 0) {
+            temp = zptr[p1];
+            zptr[p1] = zptr[p2];
+            zptr[p2] = temp;
+            p1++;
+            p2++;
+            n--;
+        }
+    }
+
+    private char med3(char a, char b, char c) {
+        char t;
+        if (a > b) {
+            t = a;
+            a = b;
+            b = t;
+        }
+        if (b > c) {
+            t = b;
+            b = c;
+            c = t;
+        }
+        if (a > b) {
+            b = a;
+        }
+        return b;
+    }
+
+    private static class StackElem {
+        int ll;
+        int hh;
+        int dd;
+    }
+
+    private void qSort3(int loSt, int hiSt, int dSt) {
+        int unLo, unHi, ltLo, gtHi, med, n, m;
+        int sp, lo, hi, d;
+        StackElem[] stack = new StackElem[QSORT_STACK_SIZE];
+        for (int count = 0; count < QSORT_STACK_SIZE; count++) {
+            stack[count] = new StackElem();
+        }
+
+        sp = 0;
+
+        stack[sp].ll = loSt;
+        stack[sp].hh = hiSt;
+        stack[sp].dd = dSt;
+        sp++;
+
+        while (sp > 0) {
+            if (sp >= QSORT_STACK_SIZE) {
+                panic();
+            }
+
+            sp--;
+            lo = stack[sp].ll;
+            hi = stack[sp].hh;
+            d = stack[sp].dd;
+
+            if (hi - lo < SMALL_THRESH || d > DEPTH_THRESH) {
+                simpleSort(lo, hi, d);
+                if (workDone > workLimit && firstAttempt) {
+                    return;
+                }
+                continue;
+            }
+
+            med = med3(block[zptr[lo] + d + 1],
+                       block[zptr[hi            ] + d  + 1],
+                       block[zptr[(lo + hi) >> 1] + d + 1]);
+
+            unLo = ltLo = lo;
+            unHi = gtHi = hi;
+
+            while (true) {
+                while (true) {
+                    if (unLo > unHi) {
+                        break;
+                    }
+                    n = ((int) block[zptr[unLo] + d + 1]) - med;
+                    if (n == 0) {
+                        int temp = 0;
+                        temp = zptr[unLo];
+                        zptr[unLo] = zptr[ltLo];
+                        zptr[ltLo] = temp;
+                        ltLo++;
+                        unLo++;
+                        continue;
+                    }
+                    if (n >  0) {
+                        break;
+                    }
+                    unLo++;
+                }
+                while (true) {
+                    if (unLo > unHi) {
+                        break;
+                    }
+                    n = ((int) block[zptr[unHi] + d + 1]) - med;
+                    if (n == 0) {
+                        int temp = 0;
+                        temp = zptr[unHi];
+                        zptr[unHi] = zptr[gtHi];
+                        zptr[gtHi] = temp;
+                        gtHi--;
+                        unHi--;
+                        continue;
+                    }
+                    if (n <  0) {
+                        break;
+                    }
+                    unHi--;
+                }
+                if (unLo > unHi) {
+                    break;
+                }
+                int temp = 0;
+                temp = zptr[unLo];
+                zptr[unLo] = zptr[unHi];
+                zptr[unHi] = temp;
+                unLo++;
+                unHi--;
+            }
+
+            if (gtHi < ltLo) {
+                stack[sp].ll = lo;
+                stack[sp].hh = hi;
+                stack[sp].dd = d + 1;
+                sp++;
+                continue;
+            }
+
+            n = ((ltLo - lo) < (unLo - ltLo)) ? (ltLo - lo) : (unLo - ltLo);
+            vswap(lo, unLo - n, n);
+            m = ((hi - gtHi) < (gtHi - unHi)) ? (hi - gtHi) : (gtHi - unHi);
+            vswap(unLo, hi - m + 1, m);
+
+            n = lo + unLo - ltLo - 1;
+            m = hi - (gtHi - unHi) + 1;
+
+            stack[sp].ll = lo;
+            stack[sp].hh = n;
+            stack[sp].dd = d;
+            sp++;
+
+            stack[sp].ll = n + 1;
+            stack[sp].hh = m - 1;
+            stack[sp].dd = d + 1;
+            sp++;
+
+            stack[sp].ll = m;
+            stack[sp].hh = hi;
+            stack[sp].dd = d;
+            sp++;
+        }
+    }
+
+    private void mainSort() {
+        int i, j, ss, sb;
+        int[] runningOrder = new int[256];
+        int[] copy = new int[256];
+        boolean[] bigDone = new boolean[256];
+        int c1, c2;
+        int numQSorted;
+
+        /*
+          In the various block-sized structures, live data runs
+          from 0 to last+NUM_OVERSHOOT_BYTES inclusive.  First,
+          set up the overshoot area for block.
+        */
+
+        //   if (verbosity >= 4) fprintf ( stderr, "   sort initialise ...\n" );
+        for (i = 0; i < NUM_OVERSHOOT_BYTES; i++) {
+            block[last + i + 2] = block[(i % (last + 1)) + 1];
+        }
+        for (i = 0; i <= last + NUM_OVERSHOOT_BYTES; i++) {
+            quadrant[i] = 0;
+        }
+
+        block[0] = (char) (block[last + 1]);
+
+        if (last < 4000) {
+            /*
+              Use simpleSort(), since the full sorting mechanism
+              has quite a large constant overhead.
+            */
+            for (i = 0; i <= last; i++) {
+                zptr[i] = i;
+            }
+            firstAttempt = false;
+            workDone = workLimit = 0;
+            simpleSort(0, last, 0);
+        } else {
+            numQSorted = 0;
+            for (i = 0; i <= 255; i++) {
+                bigDone[i] = false;
+            }
+
+            for (i = 0; i <= 65536; i++) {
+                ftab[i] = 0;
+            }
+
+            c1 = block[0];
+            for (i = 0; i <= last; i++) {
+                c2 = block[i + 1];
+                ftab[(c1 << 8) + c2]++;
+                c1 = c2;
+            }
+
+            for (i = 1; i <= 65536; i++) {
+                ftab[i] += ftab[i - 1];
+            }
+
+            c1 = block[1];
+            for (i = 0; i < last; i++) {
+                c2 = block[i + 2];
+                j = (c1 << 8) + c2;
+                c1 = c2;
+                ftab[j]--;
+                zptr[ftab[j]] = i;
+            }
+
+            j = ((block[last + 1]) << 8) + (block[1]);
+            ftab[j]--;
+            zptr[ftab[j]] = last;
+
+            /*
+              Now ftab contains the first loc of every small bucket.
+              Calculate the running order, from smallest to largest
+              big bucket.
+            */
+
+            for (i = 0; i <= 255; i++) {
+                runningOrder[i] = i;
+            }
+
+            {
+                int vv;
+                int h = 1;
+                do {
+                    h = 3 * h + 1;
+                }
+                while (h <= 256);
+                do {
+                    h = h / 3;
+                    for (i = h; i <= 255; i++) {
+                        vv = runningOrder[i];
+                        j = i;
+                        while ((ftab[((runningOrder[j - h]) + 1) << 8]
+                                - ftab[(runningOrder[j - h]) << 8]) >
+                               (ftab[((vv) + 1) << 8] - ftab[(vv) << 8])) {
+                            runningOrder[j] = runningOrder[j - h];
+                            j = j - h;
+                            if (j <= (h - 1)) {
+                                break;
+                            }
+                        }
+                        runningOrder[j] = vv;
+                    }
+                } while (h != 1);
+            }
+
+            /*
+              The main sorting loop.
+            */
+            for (i = 0; i <= 255; i++) {
+
+                /*
+                  Process big buckets, starting with the least full.
+                */
+                ss = runningOrder[i];
+
+                /*
+                  Complete the big bucket [ss] by quicksorting
+                  any unsorted small buckets [ss, j].  Hopefully
+                  previous pointer-scanning phases have already
+                  completed many of the small buckets [ss, j], so
+                  we don't have to sort them at all.
+                */
+                for (j = 0; j <= 255; j++) {
+                    sb = (ss << 8) + j;
+                    if (!((ftab[sb] & SETMASK) == SETMASK)) {
+                        int lo = ftab[sb] & CLEARMASK;
+                        int hi = (ftab[sb + 1] & CLEARMASK) - 1;
+                        if (hi > lo) {
+                            qSort3(lo, hi, 2);
+                            numQSorted += (hi - lo + 1);
+                            if (workDone > workLimit && firstAttempt) {
+                                return;
+                            }
+                        }
+                        ftab[sb] |= SETMASK;
+                    }
+                }
+
+                /*
+                  The ss big bucket is now done.  Record this fact,
+                  and update the quadrant descriptors.  Remember to
+                  update quadrants in the overshoot area too, if
+                  necessary.  The "if (i < 255)" test merely skips
+                  this updating for the last bucket processed, since
+                  updating for the last bucket is pointless.
+                */
+                bigDone[ss] = true;
+
+                if (i < 255) {
+                    int bbStart  = ftab[ss << 8] & CLEARMASK;
+                    int bbSize   = (ftab[(ss + 1) << 8] & CLEARMASK) - bbStart;
+                    int shifts   = 0;
+
+                    while ((bbSize >> shifts) > 65534) {
+                        shifts++;
+                    }
+
+                    for (j = 0; j < bbSize; j++) {
+                        int a2update = zptr[bbStart + j];
+                        int qVal = (j >> shifts);
+                        quadrant[a2update] = qVal;
+                        if (a2update < NUM_OVERSHOOT_BYTES) {
+                            quadrant[a2update + last + 1] = qVal;
+                        }
+                    }
+
+                    if (!(((bbSize - 1) >> shifts) <= 65535)) {
+                        panic();
+                    }
+                }
+
+                /*
+                  Now scan this big bucket so as to synthesise the
+                  sorted order for small buckets [t, ss] for all t != ss.
+                */
+                for (j = 0; j <= 255; j++) {
+                    copy[j] = ftab[(j << 8) + ss] & CLEARMASK;
+                }
+
+                for (j = ftab[ss << 8] & CLEARMASK;
+                     j < (ftab[(ss + 1) << 8] & CLEARMASK); j++) {
+                    c1 = block[zptr[j]];
+                    if (!bigDone[c1]) {
+                        zptr[copy[c1]] = zptr[j] == 0 ? last : zptr[j] - 1;
+                        copy[c1]++;
+                    }
+                }
+
+                for (j = 0; j <= 255; j++) {
+                    ftab[(j << 8) + ss] |= SETMASK;
+                }
+            }
+        }
+    }
+
+    private void randomiseBlock() {
+        int i;
+        int rNToGo = 0;
+        int rTPos  = 0;
+        for (i = 0; i < 256; i++) {
+            inUse[i] = false;
+        }
+
+        for (i = 0; i <= last; i++) {
+            if (rNToGo == 0) {
+                rNToGo = (char) rNums[rTPos];
+                rTPos++;
+                if (rTPos == 512) {
+                    rTPos = 0;
+                }
+            }
+            rNToGo--;
+            block[i + 1] ^= ((rNToGo == 1) ? 1 : 0);
+            // handle 16 bit signed numbers
+            block[i + 1] &= 0xFF;
+
+            inUse[block[i + 1]] = true;
+        }
+    }
+
+    private void doReversibleTransformation() {
+        int i;
+
+        workLimit = workFactor * last;
+        workDone = 0;
+        blockRandomised = false;
+        firstAttempt = true;
+
+        mainSort();
+
+        if (workDone > workLimit && firstAttempt) {
+            randomiseBlock();
+            workLimit = workDone = 0;
+            blockRandomised = true;
+            firstAttempt = false;
+            mainSort();
+        }
+
+        origPtr = -1;
+        for (i = 0; i <= last; i++) {
+            if (zptr[i] == 0) {
+                origPtr = i;
+                break;
+            }
+        }
+
+        if (origPtr == -1) {
+            panic();
+        }
+    }
+
+    private boolean fullGtU(int i1, int i2) {
+        int k;
+        char c1, c2;
+        int s1, s2;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        c1 = block[i1 + 1];
+        c2 = block[i2 + 1];
+        if (c1 != c2) {
+            return (c1 > c2);
+        }
+        i1++;
+        i2++;
+
+        k = last + 1;
+
+        do {
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            c1 = block[i1 + 1];
+            c2 = block[i2 + 1];
+            if (c1 != c2) {
+                return (c1 > c2);
+            }
+            s1 = quadrant[i1];
+            s2 = quadrant[i2];
+            if (s1 != s2) {
+                return (s1 > s2);
+            }
+            i1++;
+            i2++;
+
+            if (i1 > last) {
+                i1 -= last;
+                i1--;
+            }
+            if (i2 > last) {
+                i2 -= last;
+                i2--;
+            }
+
+            k -= 4;
+            workDone++;
+        } while (k >= 0);
+
+        return false;
+    }
+
+    /*
+      Knuth's increments seem to work better
+      than Incerpi-Sedgewick here.  Possibly
+      because the number of elems to sort is
+      usually small, typically <= 20.
+    */
+    private int[] incs = { 1, 4, 13, 40, 121, 364, 1093, 3280,
+                           9841, 29524, 88573, 265720,
+                           797161, 2391484 };
+
+    private void allocateCompressStructures () {
+        int n = baseBlockSize * blockSize100k;
+        block = new char[(n + 1 + NUM_OVERSHOOT_BYTES)];
+        quadrant = new int[(n + NUM_OVERSHOOT_BYTES)];
+        zptr = new int[n];
+        ftab = new int[65537];
+
+        if (block == null || quadrant == null || zptr == null
+            || ftab == null) {
+            //int totalDraw = (n + 1 + NUM_OVERSHOOT_BYTES) + (n + NUM_OVERSHOOT_BYTES) + n + 65537;
+            //compressOutOfMemory ( totalDraw, n );
+        }
+
+        /*
+          The back end needs a place to store the MTF values
+          whilst it calculates the coding tables.  We could
+          put them in the zptr array.  However, these values
+          will fit in a short, so we overlay szptr at the
+          start of zptr, in the hope of reducing the number
+          of cache misses induced by the multiple traversals
+          of the MTF values when calculating coding tables.
+          Seems to improve compression speed by about 1%.
+        */
+        //    szptr = zptr;
+
+
+        szptr = new short[2 * n];
+    }
+
+    private void generateMTFValues() {
+        char[] yy = new char[256];
+        int  i, j;
+        char tmp;
+        char tmp2;
+        int zPend;
+        int wr;
+        int EOB;
+
+        makeMaps();
+        EOB = nInUse + 1;
+
+        for (i = 0; i <= EOB; i++) {
+            mtfFreq[i] = 0;
+        }
+
+        wr = 0;
+        zPend = 0;
+        for (i = 0; i < nInUse; i++) {
+            yy[i] = (char) i;
+        }
+
+
+        for (i = 0; i <= last; i++) {
+            char ll_i;
+
+            ll_i = unseqToSeq[block[zptr[i]]];
+
+            j = 0;
+            tmp = yy[j];
+            while (ll_i != tmp) {
+                j++;
+                tmp2 = tmp;
+                tmp = yy[j];
+                yy[j] = tmp2;
+            }
+            yy[0] = tmp;
+
+            if (j == 0) {
+                zPend++;
+            } else {
+                if (zPend > 0) {
+                    zPend--;
+                    while (true) {
+                        switch (zPend % 2) {
+                        case 0:
+                            szptr[wr] = (short) RUNA;
+                            wr++;
+                            mtfFreq[RUNA]++;
+                            break;
+                        case 1:
+                            szptr[wr] = (short) RUNB;
+                            wr++;
+                            mtfFreq[RUNB]++;
+                            break;
+                        }
+                        if (zPend < 2) {
+                            break;
+                        }
+                        zPend = (zPend - 2) / 2;
+                    }
+                    zPend = 0;
+                }
+                szptr[wr] = (short) (j + 1);
+                wr++;
+                mtfFreq[j + 1]++;
+            }
+        }
+
+        if (zPend > 0) {
+            zPend--;
+            while (true) {
+                switch (zPend % 2) {
+                case 0:
+                    szptr[wr] = (short) RUNA;
+                    wr++;
+                    mtfFreq[RUNA]++;
+                    break;
+                case 1:
+                    szptr[wr] = (short) RUNB;
+                    wr++;
+                    mtfFreq[RUNB]++;
+                    break;
+                }
+                if (zPend < 2) {
+                    break;
+                }
+                zPend = (zPend - 2) / 2;
+            }
+        }
+
+        szptr[wr] = (short) EOB;
+        wr++;
+        mtfFreq[EOB]++;
+
+        nMTF = wr;
+    }
+}
+
+
diff --git a/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CRC.java b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CRC.java
new file mode 100644
index 0000000..ce03d28
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/apache/bzip2/CRC.java
@@ -0,0 +1,131 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.
+ *
+ */
+
+/*
+ * This package is based on the work done by Keiron Liddle, Aftex Software
+ * <keiron@aftexsw.com> to whom the Ant project is very grateful for his
+ * great code.
+ */
+
+package org.bouncycastle.apache.bzip2;
+
+/**
+ * A simple class the hold and calculate the CRC for sanity checking
+ * of the data.
+ *
+ * @author <a href="mailto:keiron@aftexsw.com">Keiron Liddle</a>
+ */
+class CRC {
+    public static int crc32Table[] = {
+        0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
+        0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
+        0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
+        0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
+        0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
+        0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
+        0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
+        0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
+        0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
+        0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
+        0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
+        0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
+        0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
+        0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
+        0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
+        0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
+        0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
+        0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
+        0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
+        0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
+        0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
+        0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
+        0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
+        0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
+        0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
+        0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
+        0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
+        0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
+        0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
+        0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
+        0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
+        0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
+        0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
+        0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
+        0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
+        0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
+        0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
+        0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
+        0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
+        0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
+        0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
+        0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
+        0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
+        0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
+        0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
+        0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
+        0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
+        0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
+        0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
+        0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
+        0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
+        0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
+        0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
+        0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
+        0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
+        0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
+        0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
+        0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
+        0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
+        0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
+        0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
+        0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
+        0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
+        0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
+    };
+
+    public CRC() {
+        initialiseCRC();
+    }
+
+    void initialiseCRC() {
+        globalCrc = 0xffffffff;
+    }
+
+    int getFinalCRC() {
+        return ~globalCrc;
+    }
+
+    int getGlobalCRC() {
+        return globalCrc;
+    }
+
+    void setGlobalCRC(int newCrc) {
+        globalCrc = newCrc;
+    }
+
+    void updateCRC(int inCh) {
+        int temp = (globalCrc >> 24) ^ inCh;
+        if (temp < 0) {
+            temp = 256 + temp;
+        }
+        globalCrc = (globalCrc << 8) ^ CRC.crc32Table[temp];
+    }
+
+    int globalCrc;
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
index 7281a6a..98ab0d6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/BERTags.java
@@ -11,9 +11,9 @@
     public static final int EXTERNAL            = 0x08;
     public static final int ENUMERATED          = 0x0a;
     public static final int SEQUENCE            = 0x10;
-    public static final int SEQUENCE_OF         = 0x10; // for completeness
+    public static final int SEQUENCE_OF         = 0x10; // for completeness - used to model a SEQUENCE of the same type.
     public static final int SET                 = 0x11;
-    public static final int SET_OF              = 0x11; // for completeness
+    public static final int SET_OF              = 0x11; // for completeness - used to model a SET of the same type.
 
 
     public static final int NUMERIC_STRING      = 0x12;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java
index 063e525..8b8d226 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERBoolean.java
@@ -160,7 +160,7 @@
     {
         if (value.length != 1)
         {
-            throw new IllegalArgumentException("byte value should have 1 byte in it");
+            throw new IllegalArgumentException("BOOLEAN value should have 1 byte in it");
         }
 
         if (value[0] == 0)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java b/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
index 2f299ee..9b1ef55 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DEREnumerated.java
@@ -5,6 +5,9 @@
 
 import org.bouncycastle.util.Arrays;
 
+/**
+ * Use ASN1Enumerated instead of this.
+ */
 public class DEREnumerated
     extends ASN1Primitive
 {
@@ -52,7 +55,7 @@
      * @exception IllegalArgumentException if the tagged object cannot
      *               be converted.
      */
-    public static DEREnumerated getInstance(
+    public static ASN1Enumerated getInstance(
         ASN1TaggedObject obj,
         boolean          explicit)
     {
@@ -68,18 +71,27 @@
         }
     }
 
+    /**
+     * @deprecated use ASN1Enumerated
+     */
     public DEREnumerated(
         int         value)
     {
         bytes = BigInteger.valueOf(value).toByteArray();
     }
 
+    /**
+     * @deprecated use ASN1Enumerated
+     */
     public DEREnumerated(
         BigInteger   value)
     {
         bytes = value.toByteArray();
     }
 
+    /**
+     * @deprecated use ASN1Enumerated
+     */
     public DEREnumerated(
         byte[]   bytes)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
index 3804450..57cc84a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERInteger.java
@@ -5,6 +5,9 @@
 
 import org.bouncycastle.util.Arrays;
 
+/**
+ * Use ASN1Integer instead of this,
+ */
 public class DERInteger
     extends ASN1Primitive
 {
@@ -67,18 +70,27 @@
         }
     }
 
+    /**
+     * @deprecated use ASN1Integer constructor
+     */
     public DERInteger(
         long         value)
     {
         bytes = BigInteger.valueOf(value).toByteArray();
     }
 
+    /**
+     * @deprecated use ASN1Integer constructor
+     */
     public DERInteger(
         BigInteger   value)
     {
         bytes = value.toByteArray();
     }
 
+    /**
+     * @deprecated use ASN1Integer constructor
+     */
     public DERInteger(
         byte[]   bytes)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
index e1de22a..3d4d04c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DERObjectIdentifier.java
@@ -6,6 +6,9 @@
 
 import org.bouncycastle.util.Arrays;
 
+/**
+ * Use ASN1ObjectIdentifier instead of this,
+ */
 public class DERObjectIdentifier
     extends ASN1Primitive
 {
@@ -38,7 +41,22 @@
 
         if (obj instanceof byte[])
         {
-            return ASN1ObjectIdentifier.fromOctetString((byte[])obj);
+            byte[] enc = (byte[])obj;
+            if (enc[0] == BERTags.OBJECT_IDENTIFIER)
+            {
+                try
+                {
+                    return (ASN1ObjectIdentifier)fromByteArray(enc);
+                }
+                catch (IOException e)
+                {
+                    throw new IllegalArgumentException("failed to construct sequence from byte[]: " + e.getMessage());
+                }
+            }
+            else
+            {    // TODO: this really shouldn't be supported here...
+                return ASN1ObjectIdentifier.fromOctetString((byte[])obj);
+            }
         }
 
         throw new IllegalArgumentException("illegal object in getInstance: " + obj.getClass().getName());
@@ -148,6 +166,9 @@
         this.body = Arrays.clone(bytes);
     }
 
+    /**
+     * @deprecated use ASN1ObjectIdentifier constructor.
+     */
     public DERObjectIdentifier(
         String identifier)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
index bb8ec4e..b5cc59a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSequence.java
@@ -3,20 +3,23 @@
 import java.io.IOException;
 import java.util.Enumeration;
 
+/**
+ * The DLSequence encodes a SEQUENCE using definite length form.
+ */
 public class DLSequence
     extends ASN1Sequence
 {
     private int bodyLength = -1;
 
     /**
-     * create an empty sequence
+     * Create an empty sequence
      */
     public DLSequence()
     {
     }
 
     /**
-     * create a sequence containing one object
+     * Create a sequence containing one object
      */
     public DLSequence(
         ASN1Encodable obj)
@@ -25,7 +28,7 @@
     }
 
     /**
-     * create a sequence containing a vector of objects.
+     * Create a sequence containing a vector of objects.
      */
     public DLSequence(
         ASN1EncodableVector v)
@@ -34,7 +37,7 @@
     }
 
     /**
-     * create a sequence containing an array of objects.
+     * Create a sequence containing an array of objects.
      */
     public DLSequence(
         ASN1Encodable[] array)
@@ -51,7 +54,7 @@
 
             for (Enumeration e = this.getObjects(); e.hasMoreElements();)
             {
-                Object    obj = e.nextElement();
+                Object obj = e.nextElement();
 
                 length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength();
             }
@@ -65,12 +68,12 @@
     int encodedLength()
         throws IOException
     {
-        int    length = getBodyLength();
+        int length = getBodyLength();
 
         return 1 + StreamUtil.calculateBodyLength(length) + length;
     }
 
-    /*
+    /**
      * A note on the implementation:
      * <p>
      * As DL requires the constructed, definite-length model to
@@ -82,17 +85,17 @@
         ASN1OutputStream out)
         throws IOException
     {
-        ASN1OutputStream       dOut = out.getDLSubStream();
-        int                    length = getBodyLength();
+        ASN1OutputStream dOut = out.getDLSubStream();
+        int length = getBodyLength();
 
         out.write(BERTags.SEQUENCE | BERTags.CONSTRUCTED);
         out.writeLength(length);
 
         for (Enumeration e = this.getObjects(); e.hasMoreElements();)
         {
-            Object    obj = e.nextElement();
+            Object obj = e.nextElement();
 
             dOut.writeObject((ASN1Encodable)obj);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
index 755754b..91e83fa 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/DLSet.java
@@ -4,7 +4,52 @@
 import java.util.Enumeration;
 
 /**
- * A DER encoded set object
+ * The DLSet encodes ASN.1 SET value without element ordering,
+ * and always using definite length form.
+ * <hr>
+ * <h2>X.690</h2>
+ * <h3>8: Basic encoding rules</h3>
+ * <h4>8.11 Encoding of a set value </h4>
+ * <b>8.11.1</b> The encoding of a set value shall be constructed
+ * <p/>
+ * <b>8.11.2</b> The contents octets shall consist of the complete
+ * encoding of a data value from each of the types listed in the
+ * ASN.1 definition of the set type, in an order chosen by the sender,
+ * unless the type was referenced with the keyword
+ * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
+ * <p/>
+ * <b>8.11.3</b> The encoding of a data value may, but need not,
+ * be present for a type which was referenced with the keyword
+ * <b>OPTIONAL</b> or the keyword <b>DEFAULT</b>.
+ * <blockquote>
+ * NOTE &mdash; The order of data values in a set value is not significant,
+ * and places no constraints on the order during transfer
+ * </blockquote>
+ * <h3>9: Canonical encoding rules</h3>
+ * <h4>9.3 Set components</h4>
+ * The encodings of the component values of a set value shall
+ * appear in an order determined by their tags as specified
+ * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
+ * Additionally, for the purposes of determining the order in which
+ * components are encoded when one or more component is an untagged
+ * choice type, each untagged choice type is ordered as though it
+ * has a tag equal to that of the smallest tag in that choice type
+ * or any untagged choice types nested within.
+ * <h3>10: Distinguished encoding rules</h3>
+ * <h4>10.3 Set components</h4>
+ * The encodings of the component values of a set value shall appear
+ * in an order determined by their tags as specified
+ * in 8.6 of ITU-T Rec. X.680 | ISO/IEC 8824-1.
+ * <blockquote>
+ * NOTE &mdash; Where a component of the set is an untagged choice type,
+ * the location of that component in the ordering will depend on
+ * the tag of the choice component being encoded.
+ * </blockquote>
+ * <h3>11: Restrictions on BER employed by both CER and DER</h3>
+ * <h4>11.5 Set and sequence components with default value </h4>
+ * The encoding of a set value or sequence value shall not include
+ * an encoding for any component value which is equal to
+ * its default value.
  */
 public class DLSet
     extends ASN1Set
@@ -54,7 +99,7 @@
 
             for (Enumeration e = this.getObjects(); e.hasMoreElements();)
             {
-                Object    obj = e.nextElement();
+                Object obj = e.nextElement();
 
                 length += ((ASN1Encodable)obj).toASN1Primitive().toDLObject().encodedLength();
             }
@@ -68,12 +113,12 @@
     int encodedLength()
         throws IOException
     {
-        int                     length = getBodyLength();
+        int length = getBodyLength();
 
         return 1 + StreamUtil.calculateBodyLength(length) + length;
     }
 
-    /*
+    /**
      * A note on the implementation:
      * <p>
      * As DL requires the constructed, definite-length model to
@@ -85,17 +130,17 @@
         ASN1OutputStream out)
         throws IOException
     {
-        ASN1OutputStream        dOut = out.getDLSubStream();
-        int                     length = getBodyLength();
+        ASN1OutputStream dOut = out.getDLSubStream();
+        int length = getBodyLength();
 
         out.write(BERTags.SET | BERTags.CONSTRUCTED);
         out.writeLength(length);
 
         for (Enumeration e = this.getObjects(); e.hasMoreElements();)
         {
-            Object    obj = e.nextElement();
+            Object obj = e.nextElement();
 
             dOut.writeObject((ASN1Encodable)obj);
         }
     }
-}
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
index 18fc66c..16a6768 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/bc/BCObjectIdentifiers.java
@@ -2,50 +2,70 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ *  iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
+ * <p>
+ *  1.3.6.1.4.1.22554
+ */
 public interface BCObjectIdentifiers
 {
     /**
      *  iso.org.dod.internet.private.enterprise.legion-of-the-bouncy-castle
-     *
+     *<p>
      *  1.3.6.1.4.1.22554
      */
     public static final ASN1ObjectIdentifier bc = new ASN1ObjectIdentifier("1.3.6.1.4.1.22554");
 
     /**
      * pbe(1) algorithms
+     * <p>
+     * 1.3.6.1.4.1.22554.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe = new ASN1ObjectIdentifier(bc.getId() + ".1");
+    public static final ASN1ObjectIdentifier bc_pbe        = bc.branch("1");
 
     /**
      * SHA-1(1)
+     * <p>
+     * 1.3.6.1.4.1.22554.1.1
      */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".1");
+    public static final ASN1ObjectIdentifier bc_pbe_sha1   = bc_pbe.branch("1");
 
-    /**
-     * SHA-2(2) . (SHA-256(1)|SHA-384(2)|SHA-512(3)|SHA-224(4))
-     */
-    public static final ASN1ObjectIdentifier bc_pbe_sha256 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.1");
-    public static final ASN1ObjectIdentifier bc_pbe_sha384 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.2");
-    public static final ASN1ObjectIdentifier bc_pbe_sha512 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.3");
-    public static final ASN1ObjectIdentifier bc_pbe_sha224 = new ASN1ObjectIdentifier(bc_pbe.getId() + ".2.4");
+    /** SHA-2.SHA-256; 1.3.6.1.4.1.22554.1.2.1 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha256 = bc_pbe.branch("2.1");
+    /** SHA-2.SHA-384; 1.3.6.1.4.1.22554.1.2.2 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha384 = bc_pbe.branch("2.2");
+    /** SHA-2.SHA-512; 1.3.6.1.4.1.22554.1.2.3 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha512 = bc_pbe.branch("2.3");
+    /** SHA-2.SHA-224; 1.3.6.1.4.1.22554.1.2.4 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha224 = bc_pbe.branch("2.4");
 
     /**
      * PKCS-5(1)|PKCS-12(2)
      */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5 = new ASN1ObjectIdentifier(bc_pbe_sha1.getId() + ".1");
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12 = new ASN1ObjectIdentifier(bc_pbe_sha1.getId() + ".2");
+    /** SHA-1.PKCS5;  1.3.6.1.4.1.22554.1.1.1 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs5    = bc_pbe_sha1.branch("1");
+    /** SHA-1.PKCS12; 1.3.6.1.4.1.22554.1.1.2 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12   = bc_pbe_sha1.branch("2");
 
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5 = new ASN1ObjectIdentifier(bc_pbe_sha256.getId() + ".1");
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = new ASN1ObjectIdentifier(bc_pbe_sha256.getId() + ".2");
+    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.1 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs5  = bc_pbe_sha256.branch("1");
+    /** SHA-256.PKCS12; 1.3.6.1.4.1.22554.1.2.1.2 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12 = bc_pbe_sha256.branch("2");
 
     /**
      * AES(1) . (CBC-128(2)|CBC-192(22)|CBC-256(42))
      */
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.2");
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.22");
-    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc = new ASN1ObjectIdentifier(bc_pbe_sha1_pkcs12.getId() + ".1.42");
+    /** 1.3.6.1.4.1.22554.1.1.2.1.2 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes128_cbc   = bc_pbe_sha1_pkcs12.branch("1.2");
+    /** 1.3.6.1.4.1.22554.1.1.2.1.22 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes192_cbc   = bc_pbe_sha1_pkcs12.branch("1.22");
+    /** 1.3.6.1.4.1.22554.1.1.2.1.42 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha1_pkcs12_aes256_cbc   = bc_pbe_sha1_pkcs12.branch("1.42");
 
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.2");
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.22");
-    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = new ASN1ObjectIdentifier(bc_pbe_sha256_pkcs12.getId() + ".1.42");
+    /** 1.3.6.1.4.1.22554.1.1.2.2.2 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes128_cbc = bc_pbe_sha256_pkcs12.branch("1.2");
+    /** 1.3.6.1.4.1.22554.1.1.2.2.22 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes192_cbc = bc_pbe_sha256_pkcs12.branch("1.22");
+    /** 1.3.6.1.4.1.22554.1.1.2.2.42 */
+    public static final ASN1ObjectIdentifier bc_pbe_sha256_pkcs12_aes256_cbc = bc_pbe_sha256_pkcs12.branch("1.42");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java
index c43afe6..51aba65 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/CMPObjectIdentifiers.java
@@ -6,10 +6,10 @@
 {
     // RFC 4210
 
-    // id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13}
+    /** id-PasswordBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 13} */
     static final ASN1ObjectIdentifier    passwordBasedMac        = new ASN1ObjectIdentifier("1.2.840.113533.7.66.13");
 
-    // id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30}
+    /** id-DHBasedMac OBJECT IDENTIFIER ::= {1 2 840 113533 7 66 30} */
     static final ASN1ObjectIdentifier    dhBasedMac              = new ASN1ObjectIdentifier("1.2.840.113533.7.66.30");
 
     // Example InfoTypeAndValue contents include, but are not limited
@@ -52,19 +52,36 @@
     //      dod(6) internet(1) security(5) mechanisms(5) pkix(7)}
     // and
     //   id-it   OBJECT IDENTIFIER ::= {id-pkix 4}
+
+    /** RFC 4120: it-id: PKIX.4 = 1.3.6.1.5.5.7.4 */
+
+    /** RFC 4120: 1.3.6.1.5.5.7.4.1 */
     static final ASN1ObjectIdentifier    it_caProtEncCert        = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.1");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.2 */
     static final ASN1ObjectIdentifier    it_signKeyPairTypes     = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.2");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.3 */
     static final ASN1ObjectIdentifier    it_encKeyPairTypes      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.3");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.4 */
     static final ASN1ObjectIdentifier    it_preferredSymAlg      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.4");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.5 */
     static final ASN1ObjectIdentifier    it_caKeyUpdateInfo      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.5");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.6 */
     static final ASN1ObjectIdentifier    it_currentCRL           = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.6");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.7 */
     static final ASN1ObjectIdentifier    it_unsupportedOIDs      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.7");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.10 */
     static final ASN1ObjectIdentifier    it_keyPairParamReq      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.10");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.11 */
     static final ASN1ObjectIdentifier    it_keyPairParamRep      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.11");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.12 */
     static final ASN1ObjectIdentifier    it_revPassphrase        = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.12");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.13 */
     static final ASN1ObjectIdentifier    it_implicitConfirm      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.13");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.14 */
     static final ASN1ObjectIdentifier    it_confirmWaitTime      = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.14");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.15 */
     static final ASN1ObjectIdentifier    it_origPKIMessage       = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.15");
+    /** RFC 4120: 1.3.6.1.5.5.7.4.16 */
     static final ASN1ObjectIdentifier    it_suppLangTags         = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.4.16");
 
     // RFC 4211
@@ -81,26 +98,44 @@
     // arc for Registration Info in CRMF
     // id-regInfo       OBJECT IDENTIFIER ::= { id-pkip id-regInfo(2) }
 
+    /** RFC 4211: it-pkip: PKIX.5 = 1.3.6.1.5.5.7.5 */
+    static final ASN1ObjectIdentifier    id_pkip                 = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5");
+
+    /** RFC 4211: it-regCtrl: 1.3.6.1.5.5.7.5.1 */
+    static final ASN1ObjectIdentifier    id_regCtrl              = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1");
+    /** RFC 4211: it-regInfo: 1.3.6.1.5.5.7.5.2 */
+    static final ASN1ObjectIdentifier    id_regInfo              = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2");
+
+
+    /** 1.3.6.1.5.5.7.5.1.1 */
     static final ASN1ObjectIdentifier    regCtrl_regToken        = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.1");
+    /** 1.3.6.1.5.5.7.5.1.2 */
     static final ASN1ObjectIdentifier    regCtrl_authenticator   = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.2");
+    /** 1.3.6.1.5.5.7.5.1.3 */
     static final ASN1ObjectIdentifier    regCtrl_pkiPublicationInfo = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.3");
+    /** 1.3.6.1.5.5.7.5.1.4 */
     static final ASN1ObjectIdentifier    regCtrl_pkiArchiveOptions  = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.4");
+    /** 1.3.6.1.5.5.7.5.1.5 */
     static final ASN1ObjectIdentifier    regCtrl_oldCertID       = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.5");
+    /** 1.3.6.1.5.5.7.5.1.6 */
     static final ASN1ObjectIdentifier    regCtrl_protocolEncrKey = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.6");
 
-    // From RFC4210:
-    // id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}
+    /** From RFC4210:
+     * id-regCtrl-altCertTemplate OBJECT IDENTIFIER ::= {id-regCtrl 7}; 1.3.6.1.5.5.7.1.7 */
     static final ASN1ObjectIdentifier    regCtrl_altCertTemplate = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.1.7");
 
+    /** RFC 4211: it-regInfo-utf8Pairs: 1.3.6.1.5.5.7.5.2.1 */
     static final ASN1ObjectIdentifier    regInfo_utf8Pairs       = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2.1");
+    /** RFC 4211: it-regInfo-certReq: 1.3.6.1.5.5.7.5.2.1 */
     static final ASN1ObjectIdentifier    regInfo_certReq         = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.5.2.2");
 
-    // id-smime OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-    //         us(840) rsadsi(113549) pkcs(1) pkcs9(9) 16 }
-    //
-    // id-ct   OBJECT IDENTIFIER ::= { id-smime  1 }  -- content types
-    //
-    // id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
+    /**
+     * 1.2.840.113549.1.9.16.1.21
+     * <p>
+     * id-ct   OBJECT IDENTIFIER ::= { id-smime  1 }  -- content types
+     * <p>
+     * id-ct-encKeyWithID OBJECT IDENTIFIER ::= {id-ct 21}
+     */
     static final ASN1ObjectIdentifier    ct_encKeyWithID         = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1.21");
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cmp/package.html
deleted file mode 100644
index eb713c9..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cmp/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting PKIX-CMP as described RFC 2510.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
index b5a2f34..066cf69 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attribute.java
@@ -10,6 +10,27 @@
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#page-14">RFC 5652</a>:
+ * Attribute is a pair of OID (as type identifier) + set of values.
+ * <p>
+ * <pre>
+ * Attribute ::= SEQUENCE {
+ *     attrType OBJECT IDENTIFIER,
+ *     attrValues SET OF AttributeValue
+ * }
+ * 
+ * AttributeValue ::= ANY
+ * </pre>
+ * <p>
+ * General rule on values is that same AttributeValue must not be included
+ * multiple times into the set. That is, if the value is a SET OF INTEGERs,
+ * then having same value repeated is wrong: (1, 1), but different values is OK: (1, 2).
+ * Normally the AttributeValue syntaxes are more complicated than that.
+ * <p>
+ * General rule of Attribute usage is that the {@link Attributes} containers
+ * must not have multiple Attribute:s with same attrType (OID) there.
+ */
 public class Attribute
     extends ASN1Object
 {
@@ -17,7 +38,14 @@
     private ASN1Set             attrValues;
 
     /**
-     * return an Attribute object from the given object.
+     * Return an Attribute object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link Attribute} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with Attribute structure inside
+     * </ul>
      *
      * @param o the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -81,12 +109,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Attribute ::= SEQUENCE {
-     *     attrType OBJECT IDENTIFIER,
-     *     attrValues SET OF AttributeValue
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
index f114623..02b6cc1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AttributeTable.java
@@ -11,6 +11,9 @@
 import org.bouncycastle.asn1.DERObjectIdentifier;
 import org.bouncycastle.asn1.DERSet;
 
+/**
+ * This is helper tool to construct {@link Attributes} sets.
+ */
 public class AttributeTable
 {
     private Hashtable attributes = new Hashtable();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
index 614e224..e21c8a7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Attributes.java
@@ -6,6 +6,21 @@
 import org.bouncycastle.asn1.ASN1Set;
 import org.bouncycastle.asn1.DLSet;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652">RFC 5652</a> defines
+ * 5 "SET OF Attribute" entities with 5 different names.
+ * This is common implementation for them all:
+ * <pre>
+ *   SignedAttributes      ::= SET SIZE (1..MAX) OF Attribute
+ *   UnsignedAttributes    ::= SET SIZE (1..MAX) OF Attribute
+ *   UnprotectedAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *   AuthAttributes        ::= SET SIZE (1..MAX) OF Attribute
+ *   UnauthAttributes      ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * Attributes ::=
+ *   SET SIZE(1..MAX) OF Attribute
+ * </pre>
+ */
 public class Attributes
     extends ASN1Object
 {
@@ -21,6 +36,19 @@
         attributes = new DLSet(v);
     }
 
+    /**
+     * Return an Attribute set object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link Attributes} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Set#getInstance(java.lang.Object) ASN1Set} input formats with Attributes structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static Attributes getInstance(Object obj)
     {
         if (obj instanceof Attributes)
@@ -47,12 +75,8 @@
         return rv;
     }
 
-    /**
-     * <pre>
-     * Attributes ::=
-     *   SET SIZE(1..MAX) OF Attribute -- according to RFC 5652
-     * </pre>
-     * @return
+    /** 
+     * Produce an object suitable for an ASN1OutputStream.
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java
index 5152dc9..034753f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedData.java
@@ -11,6 +11,27 @@
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5083">RFC 5083</a>:
+ *
+ * CMS AuthEnveloped Data object.
+ * <p>
+ * ASN.1:
+ * <pre>
+ * id-ct-authEnvelopedData OBJECT IDENTIFIER ::= { iso(1)
+ *       member-body(2) us(840) rsadsi(113549) pkcs(1) pkcs-9(9)
+ *       smime(16) ct(1) 23 }
+ *
+ * AuthEnvelopedData ::= SEQUENCE {
+ *       version CMSVersion,
+ *       originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ *       recipientInfos RecipientInfos,
+ *       authEncryptedContentInfo EncryptedContentInfo,
+ *       authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
+ *       mac MessageAuthenticationCode,
+ *       unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
+ * </pre>
+ */
 public class AuthEnvelopedData
     extends ASN1Object
 {
@@ -51,6 +72,12 @@
         this.unauthAttrs = unauthAttrs;
     }
 
+    /**
+     * Constructs AuthEnvelopedData by parsing supplied ASN1Sequence
+     * <p>
+     * @param seq An ASN1Sequence with AuthEnvelopedData
+     * @deprecated use getInstance().
+     */
     public AuthEnvelopedData(
         ASN1Sequence seq)
     {
@@ -98,8 +125,14 @@
     }
 
     /**
-     * return an AuthEnvelopedData object from a tagged object.
+     * Return an AuthEnvelopedData object from a tagged object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats
+     * </ul>
      *
+
      * @param obj      the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
      *                 tagged false otherwise.
@@ -114,10 +147,17 @@
     }
 
     /**
-     * return an AuthEnvelopedData object from the given object.
+     * Return an AuthEnvelopedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link AuthEnvelopedData} object
+     * <li> {@link ASN1Sequence org.bouncycastle.asn1.ASN1Sequence} input formats with AuthEnvelopedData structure inside
+     * </ul>
      *
-     * @param obj the object we want converted.
-     * @throws IllegalArgumentException if the object cannot be converted.
+     * @param obj The object we want converted.
+     * @throws IllegalArgumentException if the object cannot be converted, or was null.
      */
     public static AuthEnvelopedData getInstance(
         Object obj)
@@ -172,16 +212,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * AuthEnvelopedData ::= SEQUENCE {
-     *   version CMSVersion,
-     *   originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
-     *   recipientInfos RecipientInfos,
-     *   authEncryptedContentInfo EncryptedContentInfo,
-     *   authAttrs [1] IMPLICIT AuthAttributes OPTIONAL,
-     *   mac MessageAuthenticationCode,
-     *   unauthAttrs [2] IMPLICIT UnauthAttributes OPTIONAL }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java
index 55569a7..8460c33 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthEnvelopedDataParser.java
@@ -11,7 +11,7 @@
 import org.bouncycastle.asn1.BERTags;
 
 /**
- * Produce an object suitable for an ASN1OutputStream.
+ * Parse {@link AuthEnvelopedData} input stream.
  * 
  * <pre>
  * AuthEnvelopedData ::= SEQUENCE {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java
index bbf98f1..c0945f3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedData.java
@@ -14,6 +14,30 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-9.1">RFC 5652</a> section 9.1:
+ * The AuthenticatedData carries AuthAttributes and other data
+ * which define what really is being signed.
+ * <p>
+ * <pre>
+ * AuthenticatedData ::= SEQUENCE {
+ *       version CMSVersion,
+ *       originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ *       recipientInfos RecipientInfos,
+ *       macAlgorithm MessageAuthenticationCodeAlgorithm,
+ *       digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
+ *       encapContentInfo EncapsulatedContentInfo,
+ *       authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
+ *       mac MessageAuthenticationCode,
+ *       unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
+ *
+ * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
+ *
+ * MessageAuthenticationCode ::= OCTET STRING
+ * </pre>
+ */
 public class AuthenticatedData
     extends ASN1Object
 {
@@ -57,6 +81,9 @@
         this.unauthAttrs = unauthAttrs;
     }
 
+    /**
+     * @deprecated use getInstance()
+     */
     public AuthenticatedData(
         ASN1Sequence seq)
     {
@@ -102,7 +129,7 @@
     }
 
     /**
-     * return an AuthenticatedData object from a tagged object.
+     * Return an AuthenticatedData object from a tagged object.
      *
      * @param obj      the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -118,7 +145,14 @@
     }
 
     /**
-     * return an AuthenticatedData object from the given object.
+     * Return an AuthenticatedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link AuthenticatedData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with AuthenticatedData structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @throws IllegalArgumentException if the object cannot be converted.
@@ -186,24 +220,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * AuthenticatedData ::= SEQUENCE {
-     *       version CMSVersion,
-     *       originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
-     *       recipientInfos RecipientInfos,
-     *       macAlgorithm MessageAuthenticationCodeAlgorithm,
-     *       digestAlgorithm [1] DigestAlgorithmIdentifier OPTIONAL,
-     *       encapContentInfo EncapsulatedContentInfo,
-     *       authAttrs [2] IMPLICIT AuthAttributes OPTIONAL,
-     *       mac MessageAuthenticationCode,
-     *       unauthAttrs [3] IMPLICIT UnauthAttributes OPTIONAL }
-     *
-     * AuthAttributes ::= SET SIZE (1..MAX) OF Attribute
-     *
-     * UnauthAttributes ::= SET SIZE (1..MAX) OF Attribute
-     *
-     * MessageAuthenticationCode ::= OCTET STRING
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
index fd867e2..ce9aa4f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/AuthenticatedDataParser.java
@@ -13,7 +13,7 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
- * Produce an object suitable for an ASN1OutputStream.
+ * Parse {@link AuthenticatedData} stream.
  * <pre>
  * AuthenticatedData ::= SEQUENCE {
  *       version CMSVersion,
@@ -127,9 +127,18 @@
         return null;
     }
 
+    /**
+     * @deprecated use getEncapsulatedContentInfo()
+     */
     public ContentInfoParser getEnapsulatedContentInfo()
         throws IOException
     {
+        return getEncapsulatedContentInfo();
+    }
+
+    public ContentInfoParser getEncapsulatedContentInfo()
+        throws IOException
+    {
         if (nextObject == null)
         {
             nextObject = seq.readObject();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CCMParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CCMParameters.java
new file mode 100644
index 0000000..3277bb2
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CCMParameters.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <a href="http://tools.ietf.org/html/rfc5084">RFC 5084</a>: CCMParameters object.
+ * <p>
+ * <pre>
+ CCMParameters ::= SEQUENCE {
+   aes-nonce        OCTET STRING, -- recommended size is 12 octets
+   aes-ICVlen       AES-CCM-ICVlen DEFAULT 12 }
+ * </pre>
+ */
+public class CCMParameters
+    extends ASN1Object
+{
+    private byte[] nonce;
+    private int icvLen;
+
+    /**
+     * Return an CCMParameters object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link org.bouncycastle.asn1.cms.CCMParameters} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with CCMParameters structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
+    public static CCMParameters getInstance(
+        Object  obj)
+    {
+        if (obj instanceof CCMParameters)
+        {
+            return (CCMParameters)obj;
+        }
+        else if (obj != null)
+        {
+            return new CCMParameters(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    private CCMParameters(
+        ASN1Sequence seq)
+    {
+        this.nonce = ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets();
+
+        if (seq.size() == 2)
+        {
+            this.icvLen = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue().intValue();
+        }
+        else
+        {
+            this.icvLen = 12;
+        }
+    }
+
+    public CCMParameters(
+        byte[] nonce,
+        int icvLen)
+    {
+        this.nonce = Arrays.clone(nonce);
+        this.icvLen = icvLen;
+    }
+
+    public byte[] getNonce()
+    {
+        return Arrays.clone(nonce);
+    }
+
+    public int getIcvLen()
+    {
+        return icvLen;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector    v = new ASN1EncodableVector();
+
+        v.add(new DEROctetString(nonce));
+
+        if (icvLen != 12)
+        {
+            v.add(new ASN1Integer(icvLen));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
index 5e97324..d2fc7d1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSAttributes.java
@@ -3,11 +3,28 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652">RFC 5652</a> CMS attribute OID constants.
+ * <pre>
+ * contentType      ::= 1.2.840.113549.1.9.3
+ * messageDigest    ::= 1.2.840.113549.1.9.4
+ * signingTime      ::= 1.2.840.113549.1.9.5
+ * counterSignature ::= 1.2.840.113549.1.9.6
+ *
+ * contentHint      ::= 1.2.840.113549.1.9.16.2.4 
+ * </pre>
+ */
+
 public interface CMSAttributes
 {
+    /** PKCS#9: 1.2.840.113549.1.9.3 */
     public static final ASN1ObjectIdentifier  contentType = PKCSObjectIdentifiers.pkcs_9_at_contentType;
+    /** PKCS#9: 1.2.840.113549.1.9.4 */
     public static final ASN1ObjectIdentifier  messageDigest = PKCSObjectIdentifiers.pkcs_9_at_messageDigest;
+    /** PKCS#9: 1.2.840.113549.1.9.5 */
     public static final ASN1ObjectIdentifier  signingTime = PKCSObjectIdentifiers.pkcs_9_at_signingTime;
+    /** PKCS#9: 1.2.840.113549.1.9.6 */
     public static final ASN1ObjectIdentifier  counterSignature = PKCSObjectIdentifiers.pkcs_9_at_counterSignature;
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
     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
index 6294d97..b88bf6e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CMSObjectIdentifiers.java
@@ -5,24 +5,39 @@
 
 public interface CMSObjectIdentifiers
 {
+    /** PKCS#7: 1.2.840.113549.1.7.1 */
     static final ASN1ObjectIdentifier    data = PKCSObjectIdentifiers.data;
+    /** PKCS#7: 1.2.840.113549.1.7.2 */
     static final ASN1ObjectIdentifier    signedData = PKCSObjectIdentifiers.signedData;
+    /** PKCS#7: 1.2.840.113549.1.7.3 */
     static final ASN1ObjectIdentifier    envelopedData = PKCSObjectIdentifiers.envelopedData;
+    /** PKCS#7: 1.2.840.113549.1.7.4 */
     static final ASN1ObjectIdentifier    signedAndEnvelopedData = PKCSObjectIdentifiers.signedAndEnvelopedData;
+    /** PKCS#7: 1.2.840.113549.1.7.5 */
     static final ASN1ObjectIdentifier    digestedData = PKCSObjectIdentifiers.digestedData;
+    /** PKCS#7: 1.2.840.113549.1.7.6 */
     static final ASN1ObjectIdentifier    encryptedData = PKCSObjectIdentifiers.encryptedData;
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.2 -- smime ct authData */
     static final ASN1ObjectIdentifier    authenticatedData = PKCSObjectIdentifiers.id_ct_authData;
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.9 -- smime ct compressedData */
     static final ASN1ObjectIdentifier    compressedData = PKCSObjectIdentifiers.id_ct_compressedData;
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.23 -- smime ct authEnvelopedData */
     static final ASN1ObjectIdentifier    authEnvelopedData = PKCSObjectIdentifiers.id_ct_authEnvelopedData;
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.31 -- smime ct timestampedData*/
     static final ASN1ObjectIdentifier    timestampedData = PKCSObjectIdentifiers.id_ct_timestampedData;
 
     /**
      * The other Revocation Info arc
+     * <p>
+     * <pre>
      * id-ri OBJECT IDENTIFIER ::= { iso(1) identified-organization(3)
-     *                                   dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) }
+     *        dod(6) internet(1) security(5) mechanisms(5) pkix(7) ri(16) }
+     * </pre>
      */
     static final ASN1ObjectIdentifier    id_ri = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.16");
 
+    /** 1.3.6.1.5.5.7.16.2 */
     static final ASN1ObjectIdentifier    id_ri_ocsp_response = id_ri.branch("2");
+    /** 1.3.6.1.5.5.7.16.4 */
     static final ASN1ObjectIdentifier    id_ri_scvp = id_ri.branch("4");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
index e9d9f67..e546470 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedData.java
@@ -10,12 +10,13 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /** 
- * RFC 3274 - CMS Compressed Data.
+ * <a href="http://tools.ietf.org/html/rfc3274">RFC 3274</a>: CMS Compressed Data.
+ * 
  * <pre>
  * CompressedData ::= SEQUENCE {
- *  version CMSVersion,
- *  compressionAlgorithm CompressionAlgorithmIdentifier,
- *  encapContentInfo EncapsulatedContentInfo
+ *     version CMSVersion,
+ *     compressionAlgorithm CompressionAlgorithmIdentifier,
+ *     encapContentInfo EncapsulatedContentInfo
  * }
  * </pre>
  */
@@ -41,27 +42,33 @@
         this.version = (ASN1Integer)seq.getObjectAt(0);
         this.compressionAlgorithm = AlgorithmIdentifier.getInstance(seq.getObjectAt(1));
         this.encapContentInfo = ContentInfo.getInstance(seq.getObjectAt(2));
-
     }
 
     /**
-     * return a CompressedData object from a tagged object.
+     * Return a CompressedData object from a tagged object.
      *
-     * @param _ato the tagged object holding the object we want.
-     * @param _explicit true if the object is meant to be explicitly
+     * @param ato the tagged object holding the object we want.
+     * @param isExplicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the object held by the
      *          tagged object cannot be converted.
      */
     public static CompressedData getInstance(
-        ASN1TaggedObject _ato,
-        boolean _explicit)
+        ASN1TaggedObject ato,
+        boolean isExplicit)
     {
-        return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
+        return getInstance(ASN1Sequence.getInstance(ato, isExplicit));
     }
     
     /**
-     * return a CompressedData object from the given object.
+     * Return a CompressedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link CompressedData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with CompressedData structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java
index 035e19d..41895ce 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/CompressedDataParser.java
@@ -7,12 +7,13 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
- * RFC 3274 - CMS Compressed Data.
+ * Parser of <a href="http://tools.ietf.org/html/rfc3274">RFC 3274</a> {@link CompressedData} object.
+ * <p>
  * <pre>
  * CompressedData ::= SEQUENCE {
- *  version CMSVersion,
- *  compressionAlgorithm CompressionAlgorithmIdentifier,
- *  encapContentInfo EncapsulatedContentInfo
+ *     version CMSVersion,
+ *     compressionAlgorithm CompressionAlgorithmIdentifier,
+ *     encapContentInfo EncapsulatedContentInfo
  * }
  * </pre>
  */
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
index 345cf2c..2e8e039 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfo.java
@@ -10,6 +10,22 @@
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.BERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-3">RFC 5652</a> ContentInfo, and 
+ * <a href="http://tools.ietf.org/html/rfc5652#section-5.2">RFC 5652</a> EncapsulatedContentInfo objects.
+ *
+ * <pre>
+ * ContentInfo ::= SEQUENCE {
+ *     contentType ContentType,
+ *     content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL
+ * }
+ *
+ * EncapsulatedContentInfo ::= SEQUENCE {
+ *     eContentType ContentType,
+ *     eContent [0] EXPLICIT OCTET STRING OPTIONAL
+ * }
+ * </pre>
+ */
 public class ContentInfo
     extends ASN1Object
     implements CMSObjectIdentifiers
@@ -17,6 +33,19 @@
     private ASN1ObjectIdentifier contentType;
     private ASN1Encodable        content;
 
+    /**
+     * Return an ContentInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link ContentInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with ContentInfo structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static ContentInfo getInstance(
         Object  obj)
     {
@@ -84,12 +113,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * ContentInfo ::= SEQUENCE {
-     *          contentType ContentType,
-     *          content
-     *          [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java
index bbc3176..19f0ec8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ContentInfoParser.java
@@ -8,12 +8,12 @@
 import org.bouncycastle.asn1.ASN1TaggedObjectParser;
 
 /**
- * Produce an object suitable for an ASN1OutputStream.
+ * <a href="http://tools.ietf.org/html/rfc5652#section-3">RFC 5652</a> {@link ContentInfo} object parser.
+ *
  * <pre>
  * ContentInfo ::= SEQUENCE {
- *          contentType ContentType,
- *          content
- *          [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
+ *     contentType ContentType,
+ *     content [0] EXPLICIT ANY DEFINED BY contentType OPTIONAL }
  * </pre>
  */
 public class ContentInfoParser
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java
index 32b7e40..0f3b906 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/DigestedData.java
@@ -12,13 +12,13 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /** 
- * RFC 3274 - CMS Digest Data.
+ * <a href="http://tools.ietf.org/html/rfc5652#section-7">RFC 5652</a> DigestedData object.
  * <pre>
  * DigestedData ::= SEQUENCE {
- *               version CMSVersion,
- *               digestAlgorithm DigestAlgorithmIdentifier,
- *               encapContentInfo EncapsulatedContentInfo,
- *               digest Digest }
+ *       version CMSVersion,
+ *       digestAlgorithm DigestAlgorithmIdentifier,
+ *       encapContentInfo EncapsulatedContentInfo,
+ *       digest Digest }
  * </pre>
  */
 public class DigestedData
@@ -50,23 +50,30 @@
     }
 
     /**
-     * return a CompressedData object from a tagged object.
+     * Return a DigestedData object from a tagged object.
      *
-     * @param _ato the tagged object holding the object we want.
-     * @param _explicit true if the object is meant to be explicitly
+     * @param ato the tagged object holding the object we want.
+     * @param isExplicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the object held by the
      *          tagged object cannot be converted.
      */
     public static DigestedData getInstance(
-        ASN1TaggedObject _ato,
-        boolean _explicit)
+        ASN1TaggedObject ato,
+        boolean isExplicit)
     {
-        return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
+        return getInstance(ASN1Sequence.getInstance(ato, isExplicit));
     }
     
     /**
-     * return a CompressedData object from the given object.
+     * Return a DigestedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link DigestedData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
index 14265e5..64d887d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfo.java
@@ -11,6 +11,17 @@
 import org.bouncycastle.asn1.BERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> EncryptedContentInfo object.
+ *
+ * <pre>
+ * EncryptedContentInfo ::= SEQUENCE {
+ *     contentType ContentType,
+ *     contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
+ *     encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
+ * }
+ * </pre>
+ */
 public class EncryptedContentInfo
     extends ASN1Object
 {
@@ -47,7 +58,14 @@
     }
 
     /**
-     * return an EncryptedContentInfo object from the given object.
+     * Return an EncryptedContentInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link EncryptedContentInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -84,13 +102,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * EncryptedContentInfo ::= SEQUENCE {
-     *     contentType ContentType,
-     *     contentEncryptionAlgorithm ContentEncryptionAlgorithmIdentifier,
-     *     encryptedContent [0] IMPLICIT EncryptedContent OPTIONAL 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java
index 1e6f040..77fb0bb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedContentInfoParser.java
@@ -9,6 +9,8 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
+ * Parser for <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> EncryptedContentInfo object.
+ * <p>
  * <pre>
  * EncryptedContentInfo ::= SEQUENCE {
  *     contentType ContentType,
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java
index 9d61b33..2c83958 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EncryptedData.java
@@ -9,6 +9,16 @@
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.BERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-8">RFC 5652</a> EncryptedData object.
+ * <p>
+ * <pre>
+ * EncryptedData ::= SEQUENCE {
+ *     version CMSVersion,
+ *     encryptedContentInfo EncryptedContentInfo,
+ *     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
+ * </pre>
+ */
 public class EncryptedData
     extends ASN1Object
 {
@@ -16,6 +26,19 @@
     private EncryptedContentInfo encryptedContentInfo;
     private ASN1Set unprotectedAttrs;
 
+    /**
+     * Return an EncryptedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link EncryptedData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats
+     * </ul>
+     *
+     * @param o the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static EncryptedData getInstance(Object o)
     {
         if (o instanceof EncryptedData)
@@ -70,12 +93,6 @@
     }
 
     /**
-     * <pre>
-     *       EncryptedData ::= SEQUENCE {
-     *                     version CMSVersion,
-     *                     encryptedContentInfo EncryptedContentInfo,
-     *                     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL }
-     * </pre>
      * @return a basic ASN.1 object representation.
      */
     public ASN1Primitive toASN1Primitive()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
index 6d8b484..994575a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedData.java
@@ -12,6 +12,18 @@
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> EnvelopedData object.
+ * <pre>
+ * EnvelopedData ::= SEQUENCE {
+ *     version CMSVersion,
+ *     originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
+ *     recipientInfos RecipientInfos,
+ *     encryptedContentInfo EncryptedContentInfo,
+ *     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL 
+ * }
+ * </pre>
+ */
 public class EnvelopedData
     extends ASN1Object
 {
@@ -78,7 +90,7 @@
     }
     
     /**
-     * return an EnvelopedData object from a tagged object.
+     * Return an EnvelopedData object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -94,7 +106,14 @@
     }
     
     /**
-     * return an EnvelopedData object from the given object.
+     * Return an EnvelopedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link EnvelopedData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with EnvelopedData structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -142,15 +161,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * EnvelopedData ::= SEQUENCE {
-     *     version CMSVersion,
-     *     originatorInfo [0] IMPLICIT OriginatorInfo OPTIONAL,
-     *     recipientInfos RecipientInfos,
-     *     encryptedContentInfo EncryptedContentInfo,
-     *     unprotectedAttrs [1] IMPLICIT UnprotectedAttributes OPTIONAL 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java
index 73529fd..774813a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/EnvelopedDataParser.java
@@ -10,6 +10,8 @@
 import org.bouncycastle.asn1.BERTags;
 
 /** 
+ * Parser of <a href="http://tools.ietf.org/html/rfc5652#section-6.1">RFC 5652</a> {@link EnvelopedData} object.
+ * <p>
  * <pre>
  * EnvelopedData ::= SEQUENCE {
  *     version CMSVersion,
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java
index c68ec9a..4dcbfde 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Evidence.java
@@ -6,6 +6,18 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>:
+ * Binding Documents with Time-Stamps; Evidence object.
+ * <p>
+ * <pre>
+ * Evidence ::= CHOICE {
+ *     tstEvidence    [0] TimeStampTokenEvidence,   -- see RFC 3161
+ *     ersEvidence    [1] EvidenceRecord,           -- see RFC 4998
+ *     otherEvidence  [2] OtherEvidence
+ * }
+ * </pre>
+ */
 public class Evidence
     extends ASN1Object
     implements ASN1Choice
@@ -25,6 +37,18 @@
         }
     }
 
+    /**
+     * Return an Evidence object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> {@link Evidence} object
+     * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} input formats with Evidence data inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static Evidence getInstance(Object obj)
     {
         if (obj == null || obj instanceof Evidence)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java
new file mode 100644
index 0000000..0f03c87
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/GCMParameters.java
@@ -0,0 +1,102 @@
+package org.bouncycastle.asn1.cms;
+
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1OctetString;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * <a href="http://tools.ietf.org/html/rfc5084">RFC 5084</a>: GCMParameters object.
+ * <p>
+ * <pre>
+ GCMParameters ::= SEQUENCE {
+   aes-nonce        OCTET STRING, -- recommended size is 12 octets
+   aes-ICVlen       AES-GCM-ICVlen DEFAULT 12 }
+ * </pre>
+ */
+public class GCMParameters
+    extends ASN1Object
+{
+    private byte[] nonce;
+    private int icvLen;
+
+    /**
+     * Return an GCMParameters object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link org.bouncycastle.asn1.cms.GCMParameters} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(Object) ASN1Sequence} input formats with GCMParameters structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
+    public static GCMParameters getInstance(
+        Object  obj)
+    {
+        if (obj instanceof GCMParameters)
+        {
+            return (GCMParameters)obj;
+        }
+        else if (obj != null)
+        {
+            return new GCMParameters(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    private GCMParameters(
+        ASN1Sequence seq)
+    {
+        this.nonce = ASN1OctetString.getInstance(seq.getObjectAt(0)).getOctets();
+
+        if (seq.size() == 2)
+        {
+            this.icvLen = ASN1Integer.getInstance(seq.getObjectAt(1)).getValue().intValue();
+        }
+        else
+        {
+            this.icvLen = 12;
+        }
+    }
+
+    public GCMParameters(
+        byte[] nonce,
+        int    icvLen)
+    {
+        this.nonce = Arrays.clone(nonce);
+        this.icvLen = icvLen;
+    }
+
+    public byte[] getNonce()
+    {
+        return Arrays.clone(nonce);
+    }
+
+    public int getIcvLen()
+    {
+        return icvLen;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector    v = new ASN1EncodableVector();
+
+        v.add(new DEROctetString(nonce));
+
+        if (icvLen != 12)
+        {
+            v.add(new ASN1Integer(icvLen));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
index ad0dbb1..d46cbfb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/IssuerAndSerialNumber.java
@@ -13,12 +13,37 @@
 import org.bouncycastle.asn1.x509.X509CertificateStructure;
 import org.bouncycastle.asn1.x509.X509Name;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.4">RFC 5652</a>: IssuerAndSerialNumber object.
+ * <p>
+ * <pre>
+ * IssuerAndSerialNumber ::= SEQUENCE {
+ *     issuer Name,
+ *     serialNumber CertificateSerialNumber
+ * }
+ *
+ * CertificateSerialNumber ::= INTEGER  -- See RFC 5280
+ * </pre>
+ */
 public class IssuerAndSerialNumber
     extends ASN1Object
 {
     private X500Name    name;
     private ASN1Integer  serialNumber;
 
+    /**
+     * Return an IssuerAndSerialNumber object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link IssuerAndSerialNumber} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with IssuerAndSerialNumber structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static IssuerAndSerialNumber getInstance(
         Object  obj)
     {
@@ -36,7 +61,6 @@
 
     /**
      * @deprecated  use getInstance() method.
-     * @param seq
      */
     public IssuerAndSerialNumber(
         ASN1Sequence    seq)
@@ -52,6 +76,9 @@
         this.serialNumber = certificate.getSerialNumber();
     }
 
+    /**
+     * @deprecated use constructor taking Certificate
+     */
     public IssuerAndSerialNumber(
         X509CertificateStructure certificate)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
index 67c68ab..0361e9f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKIdentifier.java
@@ -10,6 +10,18 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.3">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * KEKIdentifier ::= SEQUENCE {
+ *     keyIdentifier OCTET STRING,
+ *     date GeneralizedTime OPTIONAL,
+ *     other OtherKeyAttribute OPTIONAL 
+ * }
+ * </pre>
+ */
 public class KEKIdentifier
     extends ASN1Object
 {
@@ -56,7 +68,7 @@
     }
 
     /**
-     * return a KEKIdentifier object from a tagged object.
+     * Return a KEKIdentifier object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -72,7 +84,14 @@
     }
     
     /**
-     * return a KEKIdentifier object from the given object.
+     * Return a KEKIdentifier object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link KEKIdentifier} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KEKIdentifier structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -110,13 +129,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KEKIdentifier ::= SEQUENCE {
-     *     keyIdentifier OCTET STRING,
-     *     date GeneralizedTime OPTIONAL,
-     *     other OtherKeyAttribute OPTIONAL 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
index 6c67772..2d0cfa6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KEKRecipientInfo.java
@@ -10,6 +10,19 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.3">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * KEKRecipientInfo ::= SEQUENCE {
+ *     version CMSVersion,  -- always set to 4
+ *     kekid KEKIdentifier,
+ *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ *     encryptedKey EncryptedKey 
+ * }
+ * </pre>
+ */
 public class KEKRecipientInfo
     extends ASN1Object
 {
@@ -39,7 +52,7 @@
     }
 
     /**
-     * return a KEKRecipientInfo object from a tagged object.
+     * Return a KEKRecipientInfo object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -55,7 +68,14 @@
     }
     
     /**
-     * return a KEKRecipientInfo object from the given object.
+     * Return a KEKRecipientInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link KEKRecipientInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KEKRecipientInfo structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -63,17 +83,17 @@
     public static KEKRecipientInfo getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof KEKRecipientInfo)
+        if (obj instanceof KEKRecipientInfo)
         {
             return (KEKRecipientInfo)obj;
         }
         
-        if(obj instanceof ASN1Sequence)
+        if (obj != null)
         {
-            return new KEKRecipientInfo((ASN1Sequence)obj);
+            return new KEKRecipientInfo(ASN1Sequence.getInstance(obj));
         }
         
-        throw new IllegalArgumentException("Invalid KEKRecipientInfo: " + obj.getClass().getName());
+        return null;
     }
 
     public ASN1Integer getVersion()
@@ -98,14 +118,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KEKRecipientInfo ::= SEQUENCE {
-     *     version CMSVersion,  -- always set to 4
-     *     kekid KEKIdentifier,
-     *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *     encryptedKey EncryptedKey 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
index 29f455a..6580cd4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientIdentifier.java
@@ -7,6 +7,16 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * KeyAgreeRecipientIdentifier ::= CHOICE {
+ *     issuerAndSerialNumber IssuerAndSerialNumber,
+ *     rKeyId [0] IMPLICIT RecipientKeyIdentifier }
+ * </pre>
+ */
 public class KeyAgreeRecipientIdentifier
     extends ASN1Object
     implements ASN1Choice
@@ -15,7 +25,7 @@
     private RecipientKeyIdentifier rKeyID;
 
     /**
-     * return an KeyAgreeRecipientIdentifier object from a tagged object.
+     * Return an KeyAgreeRecipientIdentifier object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -31,7 +41,16 @@
     }
     
     /**
-     * return an KeyAgreeRecipientIdentifier object from the given object.
+     * Return an KeyAgreeRecipientIdentifier object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> {@link KeyAgreeRecipientIdentifier} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with IssuerAndSerialNumber structure inside
+     * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} with tag value 0: a KeyAgreeRecipientIdentifier data structure
+     * </ul>
+     * <p>
+     * Note: no byte[] input!
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -84,12 +103,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KeyAgreeRecipientIdentifier ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     rKeyId [0] IMPLICIT RecipientKeyIdentifier
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
index c6e5744..224932a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyAgreeRecipientInfo.java
@@ -11,6 +11,22 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * KeyAgreeRecipientInfo ::= SEQUENCE {
+ *     version CMSVersion,  -- always set to 3
+ *     originator [0] EXPLICIT OriginatorIdentifierOrKey,
+ *     ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
+ *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ *     recipientEncryptedKeys RecipientEncryptedKeys 
+ * }
+ *
+ * UserKeyingMaterial ::= OCTET STRING
+ * </pre>
+ */
 public class KeyAgreeRecipientInfo
     extends ASN1Object
 {
@@ -32,7 +48,10 @@
         this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
         this.recipientEncryptedKeys = recipientEncryptedKeys;
     }
-    
+
+    /**
+     * @deprecated use getInstance()
+     */
     public KeyAgreeRecipientInfo(
         ASN1Sequence seq)
     {
@@ -55,7 +74,7 @@
     }
     
     /**
-     * return a KeyAgreeRecipientInfo object from a tagged object.
+     * Return a KeyAgreeRecipientInfo object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -71,7 +90,14 @@
     }
     
     /**
-     * return a KeyAgreeRecipientInfo object from the given object.
+     * Return a KeyAgreeRecipientInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link KeyAgreeRecipientInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KeyAgreeRecipientInfo structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -79,19 +105,17 @@
     public static KeyAgreeRecipientInfo getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof KeyAgreeRecipientInfo)
+        if (obj instanceof KeyAgreeRecipientInfo)
         {
             return (KeyAgreeRecipientInfo)obj;
         }
         
-        if (obj instanceof ASN1Sequence)
+        if (obj != null)
         {
-            return new KeyAgreeRecipientInfo((ASN1Sequence)obj);
+            return new KeyAgreeRecipientInfo(ASN1Sequence.getInstance(obj));
         }
         
-        throw new IllegalArgumentException(
-        "Illegal object in KeyAgreeRecipientInfo: " + obj.getClass().getName());
-
+        return null;
     } 
 
     public ASN1Integer getVersion()
@@ -121,17 +145,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KeyAgreeRecipientInfo ::= SEQUENCE {
-     *     version CMSVersion,  -- always set to 3
-     *     originator [0] EXPLICIT OriginatorIdentifierOrKey,
-     *     ukm [1] EXPLICIT UserKeyingMaterial OPTIONAL,
-     *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *     recipientEncryptedKeys RecipientEncryptedKeys 
-     * }
-     *
-     * UserKeyingMaterial ::= OCTET STRING
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
index 8b0a545..7d31111 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/KeyTransRecipientInfo.java
@@ -10,6 +10,18 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.1">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <pre>
+ * KeyTransRecipientInfo ::= SEQUENCE {
+ *     version CMSVersion,  -- always set to 0 or 2
+ *     rid RecipientIdentifier,
+ *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ *     encryptedKey EncryptedKey 
+ * }
+ * </pre>
+ */
 public class KeyTransRecipientInfo
     extends ASN1Object
 {
@@ -36,7 +48,10 @@
         this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
         this.encryptedKey = encryptedKey;
     }
-    
+
+    /**
+     * @deprecated use getInstance()
+     */
     public KeyTransRecipientInfo(
         ASN1Sequence seq)
     {
@@ -47,7 +62,14 @@
     }
 
     /**
-     * return a KeyTransRecipientInfo object from the given object.
+     * Return a KeyTransRecipientInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link KeyTransRecipientInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with KeyTransRecipientInfo structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -55,18 +77,17 @@
     public static KeyTransRecipientInfo getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof KeyTransRecipientInfo)
+        if (obj instanceof KeyTransRecipientInfo)
         {
             return (KeyTransRecipientInfo)obj;
         }
         
-        if(obj instanceof ASN1Sequence)
+        if(obj != null)
         {
-            return new KeyTransRecipientInfo((ASN1Sequence)obj);
+            return new KeyTransRecipientInfo(ASN1Sequence.getInstance(obj));
         }
         
-        throw new IllegalArgumentException(
-        "Illegal object in KeyTransRecipientInfo: " + obj.getClass().getName());
+        return null;
     } 
 
     public ASN1Integer getVersion()
@@ -91,14 +112,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * KeyTransRecipientInfo ::= SEQUENCE {
-     *     version CMSVersion,  -- always set to 0 or 2
-     *     rid RecipientIdentifier,
-     *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *     encryptedKey EncryptedKey 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java
index 73db22e..667187b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/MetaData.java
@@ -9,6 +9,19 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERUTF8String;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>:
+ * Binding Documents with Time-Stamps; MetaData object.
+ * <p>
+ * <pre>
+ * MetaData ::= SEQUENCE {
+ *   hashProtected        BOOLEAN,
+ *   fileName             UTF8String OPTIONAL,
+ *   mediaType            IA5String OPTIONAL,
+ *   otherMetaData        Attributes OPTIONAL
+ * }
+ * </pre>
+ */
 public class MetaData
     extends ASN1Object
 {
@@ -49,6 +62,19 @@
         }
     }
 
+    /**
+     * Return a MetaData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link MetaData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with MetaData structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static MetaData getInstance(Object obj)
     {
         if (obj instanceof MetaData)
@@ -63,17 +89,6 @@
         return null;
     }
 
-    /**
-     * <pre>
-     * MetaData ::= SEQUENCE {
-     *   hashProtected        BOOLEAN,
-     *   fileName             UTF8String OPTIONAL,
-     *   mediaType            IA5String OPTIONAL,
-     *   otherMetaData        Attributes OPTIONAL
-     * }
-     * </pre>
-     * @return
-     */
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
index c7c3ecb..2096be2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorIdentifierOrKey.java
@@ -9,6 +9,19 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <pre>
+ * OriginatorIdentifierOrKey ::= CHOICE {
+ *     issuerAndSerialNumber IssuerAndSerialNumber,
+ *     subjectKeyIdentifier [0] SubjectKeyIdentifier,
+ *     originatorKey [1] OriginatorPublicKey 
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
 public class OriginatorIdentifierOrKey
     extends ASN1Object
     implements ASN1Choice
@@ -52,7 +65,7 @@
     }
 
     /**
-     * return an OriginatorIdentifierOrKey object from a tagged object.
+     * Return an OriginatorIdentifierOrKey object from a tagged object.
      *
      * @param o the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -74,7 +87,17 @@
     }
     
     /**
-     * return an OriginatorIdentifierOrKey object from the given object.
+     * Return an OriginatorIdentifierOrKey object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link OriginatorIdentifierOrKey} object
+     * <li> {@link IssuerAndSerialNumber} object
+     * <li> {@link SubjectKeyIdentifier} object
+     * <li> {@link OriginatorPublicKey} object
+     * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} input formats with IssuerAndSerialNumber structure inside
+     * </ul>
      *
      * @param o the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -148,15 +171,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OriginatorIdentifierOrKey ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     subjectKeyIdentifier [0] SubjectKeyIdentifier,
-     *     originatorKey [1] OriginatorPublicKey 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
index d87054b..96abf7d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorInfo.java
@@ -9,6 +9,36 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.1">RFC 5652</a>: OriginatorInfo object.
+ * <pre>
+ * RFC 3369:
+ *
+ * OriginatorInfo ::= SEQUENCE {
+ *     certs [0] IMPLICIT CertificateSet OPTIONAL,
+ *     crls  [1] IMPLICIT CertificateRevocationLists OPTIONAL 
+ * }
+ * CertificateRevocationLists ::= SET OF CertificateList (from X.509)
+ *
+ * RFC 3582 / 5652:
+ *
+ * OriginatorInfo ::= SEQUENCE {
+ *     certs [0] IMPLICIT CertificateSet OPTIONAL,
+ *     crls  [1] IMPLICIT RevocationInfoChoices OPTIONAL
+ * }
+ * RevocationInfoChoices ::= SET OF RevocationInfoChoice
+ * RevocationInfoChoice ::= CHOICE {
+ *     crl CertificateList,
+ *     other [1] IMPLICIT OtherRevocationInfoFormat }
+ *
+ * OtherRevocationInfoFormat ::= SEQUENCE {
+ *     otherRevInfoFormat OBJECT IDENTIFIER,
+ *     otherRevInfo ANY DEFINED BY otherRevInfoFormat }
+ * </pre>
+ * <p>
+ * TODO: RevocationInfoChoices / RevocationInfoChoice.
+ *       Constructor using CertificateSet, CertificationInfoChoices
+ */
 public class OriginatorInfo
     extends ASN1Object
 {
@@ -54,7 +84,7 @@
     }
     
     /**
-     * return an OriginatorInfo object from a tagged object.
+     * Return an OriginatorInfo object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -70,7 +100,14 @@
     }
     
     /**
-     * return an OriginatorInfo object from the given object.
+     * Return an OriginatorInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link OriginatorInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OriginatorInfo structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -86,7 +123,7 @@
         {
             return new OriginatorInfo(ASN1Sequence.getInstance(obj));
         }
-        
+
         return null;
     }
     
@@ -102,12 +139,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OriginatorInfo ::= SEQUENCE {
-     *     certs [0] IMPLICIT CertificateSet OPTIONAL,
-     *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
index 5d95d13..b9bc52f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OriginatorPublicKey.java
@@ -9,7 +9,17 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
-
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * OriginatorPublicKey ::= SEQUENCE {
+ *     algorithm AlgorithmIdentifier,
+ *     publicKey BIT STRING 
+ * }
+ * </pre>
+ */
 public class OriginatorPublicKey
     extends ASN1Object
 {
@@ -23,7 +33,10 @@
         this.algorithm = algorithm;
         this.publicKey = new DERBitString(publicKey);
     }
-    
+
+    /**
+     * @deprecated use getInstance()
+     */
     public OriginatorPublicKey(
         ASN1Sequence seq)
     {
@@ -32,7 +45,7 @@
     }
     
     /**
-     * return an OriginatorPublicKey object from a tagged object.
+     * Return an OriginatorPublicKey object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -48,7 +61,14 @@
     }
     
     /**
-     * return an OriginatorPublicKey object from the given object.
+     * Return an OriginatorPublicKey object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link OriginatorPublicKey} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OriginatorPublicKey structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -56,17 +76,17 @@
     public static OriginatorPublicKey getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof OriginatorPublicKey)
+        if (obj instanceof OriginatorPublicKey)
         {
             return (OriginatorPublicKey)obj;
         }
         
-        if (obj instanceof ASN1Sequence)
+        if (obj != null)
         {
-            return new OriginatorPublicKey((ASN1Sequence)obj);
+            return new OriginatorPublicKey(ASN1Sequence.getInstance(obj));
         }
-        
-        throw new IllegalArgumentException("Invalid OriginatorPublicKey: " + obj.getClass().getName());
+
+        return null;
     } 
 
     public AlgorithmIdentifier getAlgorithm()
@@ -81,12 +101,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OriginatorPublicKey ::= SEQUENCE {
-     *     algorithm AlgorithmIdentifier,
-     *     publicKey BIT STRING 
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
index 1336bb6..7363c81 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherKeyAttribute.java
@@ -8,6 +8,16 @@
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.7">RFC 5652</a>: OtherKeyAttribute object.
+ * <p>
+ * <pre>
+ * OtherKeyAttribute ::= SEQUENCE {
+ *     keyAttrId OBJECT IDENTIFIER,
+ *     keyAttr ANY DEFINED BY keyAttrId OPTIONAL
+ * }
+ * </pre>
+ */
 public class OtherKeyAttribute
     extends ASN1Object
 {
@@ -15,7 +25,14 @@
     private ASN1Encodable        keyAttr;
 
     /**
-     * return an OtherKeyAttribute object from the given object.
+     * Return an OtherKeyAttribute object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link OtherKeyAttribute} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OtherKeyAttribute structure inside
+     * </ul>
      *
      * @param o the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -23,19 +40,22 @@
     public static OtherKeyAttribute getInstance(
         Object o)
     {
-        if (o == null || o instanceof OtherKeyAttribute)
+        if (o instanceof OtherKeyAttribute)
         {
             return (OtherKeyAttribute)o;
         }
         
-        if (o instanceof ASN1Sequence)
+        if (o != null)
         {
-            return new OtherKeyAttribute((ASN1Sequence)o);
+            return new OtherKeyAttribute(ASN1Sequence.getInstance(o));
         }
 
-        throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+        return null;
     }
-    
+
+    /**
+     * @deprecated use getInstance()
+     */
     public OtherKeyAttribute(
         ASN1Sequence seq)
     {
@@ -63,12 +83,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OtherKeyAttribute ::= SEQUENCE {
-     *     keyAttrId OBJECT IDENTIFIER,
-     *     keyAttr ANY DEFINED BY keyAttrId OPTIONAL
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
index 692c96c..b77b150 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRecipientInfo.java
@@ -9,6 +9,15 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.5">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <pre>
+ * OtherRecipientInfo ::= SEQUENCE {
+ *    oriType OBJECT IDENTIFIER,
+ *    oriValue ANY DEFINED BY oriType }
+ * </pre>
+ */
 public class OtherRecipientInfo
     extends ASN1Object
 {
@@ -25,7 +34,6 @@
 
     /**
      * @deprecated use getInstance().
-     * @param seq
      */
     public OtherRecipientInfo(
         ASN1Sequence seq)
@@ -35,7 +43,7 @@
     }
 
     /**
-     * return a OtherRecipientInfo object from a tagged object.
+     * Return a OtherRecipientInfo object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -51,7 +59,14 @@
     }
     
     /**
-     * return a OtherRecipientInfo object from the given object.
+     * Return a OtherRecipientInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link PasswordRecipientInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OtherRecipientInfo structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -84,11 +99,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OtherRecipientInfo ::= SEQUENCE {
-     *    oriType OBJECT IDENTIFIER,
-     *    oriValue ANY DEFINED BY oriType }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java
index ae6518a..a8348ff 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/OtherRevocationInfoFormat.java
@@ -9,6 +9,15 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.1">RFC 5652</a>: OtherRevocationInfoFormat object.
+ * <p>
+ * <pre>
+ * OtherRevocationInfoFormat ::= SEQUENCE {
+ *      otherRevInfoFormat OBJECT IDENTIFIER,
+ *      otherRevInfo ANY DEFINED BY otherRevInfoFormat }
+ * </pre>
+ */
 public class OtherRevocationInfoFormat
     extends ASN1Object
 {
@@ -31,7 +40,7 @@
     }
 
     /**
-     * return a OtherRevocationInfoFormat object from a tagged object.
+     * Return a OtherRevocationInfoFormat object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -47,7 +56,14 @@
     }
     
     /**
-     * return a OtherRevocationInfoFormat object from the given object.
+     * Return a OtherRevocationInfoFormat object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link OtherRevocationInfoFormat} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with OtherRevocationInfoFormat structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -80,11 +96,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * OtherRevocationInfoFormat ::= SEQUENCE {
-     *      otherRevInfoFormat OBJECT IDENTIFIER,
-     *      otherRevInfo ANY DEFINED BY otherRevInfoFormat }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
index f325fcd..7ed16cf 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/PasswordRecipientInfo.java
@@ -11,6 +11,18 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-10.2.7">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <pre>
+ * PasswordRecipientInfo ::= SEQUENCE {
+ *     version       CMSVersion,   -- Always set to 0
+ *     keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
+ *                             OPTIONAL,
+ *     keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
+ *     encryptedKey  EncryptedKey }
+ * </pre>
+ */
 public class PasswordRecipientInfo
     extends ASN1Object
 {
@@ -38,7 +50,10 @@
         this.keyEncryptionAlgorithm = keyEncryptionAlgorithm;
         this.encryptedKey = encryptedKey;
     }
-    
+
+    /**
+     * @deprecated use getInstance() method.
+     */
     public PasswordRecipientInfo(
         ASN1Sequence seq)
     {
@@ -57,7 +72,7 @@
     }
 
     /**
-     * return a PasswordRecipientInfo object from a tagged object.
+     * Return a PasswordRecipientInfo object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -73,7 +88,14 @@
     }
     
     /**
-     * return a PasswordRecipientInfo object from the given object.
+     * Return a PasswordRecipientInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link PasswordRecipientInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with PasswordRecipientInfo structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -81,17 +103,17 @@
     public static PasswordRecipientInfo getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof PasswordRecipientInfo)
+        if (obj instanceof PasswordRecipientInfo)
         {
             return (PasswordRecipientInfo)obj;
         }
         
-        if(obj instanceof ASN1Sequence)
+        if (obj != null)
         {
-            return new PasswordRecipientInfo((ASN1Sequence)obj);
+            return new PasswordRecipientInfo(ASN1Sequence.getInstance(obj));
         }
         
-        throw new IllegalArgumentException("Invalid PasswordRecipientInfo: " + obj.getClass().getName());
+        return null;
     }
 
     public ASN1Integer getVersion()
@@ -116,14 +138,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * PasswordRecipientInfo ::= SEQUENCE {
-     *   version CMSVersion,   -- Always set to 0
-     *   keyDerivationAlgorithm [0] KeyDerivationAlgorithmIdentifier
-     *                             OPTIONAL,
-     *  keyEncryptionAlgorithm KeyEncryptionAlgorithmIdentifier,
-     *  encryptedKey EncryptedKey }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java
index 2f2a173..5062c10 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientEncryptedKey.java
@@ -8,7 +8,16 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSequence;
 
-
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <pre>
+ * RecipientEncryptedKey ::= SEQUENCE {
+ *     rid KeyAgreeRecipientIdentifier,
+ *     encryptedKey EncryptedKey
+ * }
+ * </pre>
+ */
 public class RecipientEncryptedKey
     extends ASN1Object
 {
@@ -23,7 +32,7 @@
     }
     
     /**
-     * return an RecipientEncryptedKey object from a tagged object.
+     * Return an RecipientEncryptedKey object from a tagged object.
      *
      * @param obj the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -39,7 +48,14 @@
     }
     
     /**
-     * return a RecipientEncryptedKey object from the given object.
+     * Return a RecipientEncryptedKey object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link RecipientEncryptedKey} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with RecipientEncryptedKey structure inside
+     * </ul>
      *
      * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -47,17 +63,17 @@
     public static RecipientEncryptedKey getInstance(
         Object obj)
     {
-        if (obj == null || obj instanceof RecipientEncryptedKey)
+        if (obj instanceof RecipientEncryptedKey)
         {
             return (RecipientEncryptedKey)obj;
         }
         
-        if (obj instanceof ASN1Sequence)
+        if (obj != null)
         {
-            return new RecipientEncryptedKey((ASN1Sequence)obj);
+            return new RecipientEncryptedKey(ASN1Sequence.getInstance(obj));
         }
         
-        throw new IllegalArgumentException("Invalid RecipientEncryptedKey: " + obj.getClass().getName());
+        return null;
     } 
 
     public RecipientEncryptedKey(
@@ -80,12 +96,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientEncryptedKey ::= SEQUENCE {
-     *     rid KeyAgreeRecipientIdentifier,
-     *     encryptedKey EncryptedKey
-     * }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
index 8aa992d..66b154a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientIdentifier.java
@@ -8,6 +8,18 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.1">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <pre>
+ * RecipientIdentifier ::= CHOICE {
+ *     issuerAndSerialNumber IssuerAndSerialNumber,
+ *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
 public class RecipientIdentifier
     extends ASN1Object
     implements ASN1Choice
@@ -33,7 +45,16 @@
     }
     
     /**
-     * return a RecipientIdentifier object from the given object.
+     * Return a RecipientIdentifier object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link RecipientIdentifier} object
+     * <li> {@link IssuerAndSerialNumber} object
+     * <li> {@link org.bouncycastle.asn1.ASN1OctetString#getInstance(java.lang.Object) ASN1OctetString} input formats (OctetString, byte[]) with value of KeyIdentifier in DER form
+     * <li> {@link org.bouncycastle.asn1.ASN1Primitive ASN1Primitive} for RecipientIdentifier constructor
+     * </ul>
      *
      * @param o the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -82,14 +103,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientIdentifier ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
index 7593a7a..39a7bb2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientInfo.java
@@ -9,6 +9,19 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * RecipientInfo ::= CHOICE {
+ *     ktri      KeyTransRecipientInfo,
+ *     kari  [1] KeyAgreeRecipientInfo,
+ *     kekri [2] KEKRecipientInfo,
+ *     pwri  [3] PasswordRecipientInfo,
+ *     ori   [4] OtherRecipientInfo }
+ * </pre>
+ */
 public class RecipientInfo
     extends ASN1Object
     implements ASN1Choice
@@ -51,6 +64,20 @@
         this.info = info;
     }
 
+    /**
+     * Return a RecipientInfo object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link RecipientInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with RecipientInfo structure inside
+     * <li> {@link org.bouncycastle.asn1.ASN1TaggedObject#getInstance(java.lang.Object) ASN1TaggedObject} input formats with RecipientInfo structure inside
+     * </ul>
+     *
+     * @param o the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static RecipientInfo getInstance(
         Object  o)
     {
@@ -138,14 +165,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientInfo ::= CHOICE {
-     *     ktri KeyTransRecipientInfo,
-     *     kari [1] KeyAgreeRecipientInfo,
-     *     kekri [2] KEKRecipientInfo,
-     *     pwri [3] PasswordRecipientInfo,
-     *     ori [4] OtherRecipientInfo }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
index 076761b..f0eae59 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/RecipientKeyIdentifier.java
@@ -10,6 +10,20 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-6.2.2">RFC 5652</a>:
+ * Content encryption key delivery mechanisms.
+ * <p>
+ * <pre>
+ * RecipientKeyIdentifier ::= SEQUENCE {
+ *     subjectKeyIdentifier SubjectKeyIdentifier,
+ *     date GeneralizedTime OPTIONAL,
+ *     other OtherKeyAttribute OPTIONAL 
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
 public class RecipientKeyIdentifier
     extends ASN1Object
 {
@@ -43,6 +57,9 @@
         this(subjectKeyIdentifier, null, null);
     }
 
+    /**
+     * @deprecated use getInstance()
+     */
     public RecipientKeyIdentifier(
         ASN1Sequence seq)
     {
@@ -73,38 +90,45 @@
     }
 
     /**
-     * return a RecipientKeyIdentifier object from a tagged object.
+     * Return a RecipientKeyIdentifier object from a tagged object.
      *
-     * @param _ato the tagged object holding the object we want.
-     * @param _explicit true if the object is meant to be explicitly
+     * @param ato the tagged object holding the object we want.
+     * @param isExplicit true if the object is meant to be explicitly
      *              tagged false otherwise.
      * @exception IllegalArgumentException if the object held by the
      *          tagged object cannot be converted.
      */
-    public static RecipientKeyIdentifier getInstance(ASN1TaggedObject _ato, boolean _explicit)
+    public static RecipientKeyIdentifier getInstance(ASN1TaggedObject ato, boolean isExplicit)
     {
-        return getInstance(ASN1Sequence.getInstance(_ato, _explicit));
+        return getInstance(ASN1Sequence.getInstance(ato, isExplicit));
     }
     
     /**
-     * return a RecipientKeyIdentifier object from the given object.
+     * Return a RecipientKeyIdentifier object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link RecipientKeyIdentifier} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with RecipientKeyIdentifier structure inside
+     * </ul>
      *
-     * @param _obj the object we want converted.
+     * @param obj the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
      */
-    public static RecipientKeyIdentifier getInstance(Object _obj)
+    public static RecipientKeyIdentifier getInstance(Object obj)
     {
-        if(_obj == null || _obj instanceof RecipientKeyIdentifier)
+        if (obj instanceof RecipientKeyIdentifier)
         {
-            return (RecipientKeyIdentifier)_obj;
+            return (RecipientKeyIdentifier)obj;
         }
         
-        if(_obj instanceof ASN1Sequence)
+        if(obj != null)
         {
-            return new RecipientKeyIdentifier((ASN1Sequence)_obj);
+            return new RecipientKeyIdentifier(ASN1Sequence.getInstance(obj));
         }
         
-        throw new IllegalArgumentException("Invalid RecipientKeyIdentifier: " + _obj.getClass().getName());
+        return null;
     } 
 
     public ASN1OctetString getSubjectKeyIdentifier()
@@ -125,15 +149,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * RecipientKeyIdentifier ::= SEQUENCE {
-     *     subjectKeyIdentifier SubjectKeyIdentifier,
-     *     date GeneralizedTime OPTIONAL,
-     *     other OtherKeyAttribute OPTIONAL 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java
index e9b91eb..52279c3 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SCVPReqRes.java
@@ -8,12 +8,35 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5940">RFC 5940</a>:
+ * Additional Cryptographic Message Syntax (CMS) Revocation Information Choices.
+ * <p>
+ * <pre>
+ * SCVPReqRes ::= SEQUENCE {
+ *     request  [0] EXPLICIT ContentInfo OPTIONAL,
+ *     response     ContentInfo }
+ * </pre>
+ */
 public class SCVPReqRes
     extends ASN1Object
 {
     private final ContentInfo request;
     private final ContentInfo response;
 
+    /**
+     * Return a SCVPReqRes object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link SCVPReqRes} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SCVPReqRes structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static SCVPReqRes getInstance(
         Object  obj)
     {
@@ -67,11 +90,6 @@
     }
 
     /**
-     * <pre>
-     *    SCVPReqRes ::= SEQUENCE {
-     *    request  [0] EXPLICIT ContentInfo OPTIONAL,
-     *    response     ContentInfo }
-     * </pre>
      * @return  the ASN.1 primitive representation.
      */
     public ASN1Primitive toASN1Primitive()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
index fd2718a..8c7fcc2 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedData.java
@@ -16,7 +16,45 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 
 /**
- * a signed data object.
+ * <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>:
+ * <p>
+ * A signed data object containing multitude of {@link SignerInfo}s.
+ * <pre>
+ * SignedData ::= SEQUENCE {
+ *     version CMSVersion,
+ *     digestAlgorithms DigestAlgorithmIdentifiers,
+ *     encapContentInfo EncapsulatedContentInfo,
+ *     certificates [0] IMPLICIT CertificateSet OPTIONAL,
+ *     crls [1] IMPLICIT CertificateRevocationLists OPTIONAL,
+ *     signerInfos SignerInfos
+ *   }
+ * 
+ * DigestAlgorithmIdentifiers ::= SET OF DigestAlgorithmIdentifier
+ * 
+ * SignerInfos ::= SET OF SignerInfo
+ * </pre>
+ * <p>
+ * The version calculation uses following ruleset from RFC 3852 section 5.1:
+ * <pre>
+ * 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
+ * </pre>
+ * <p>
+ * @todo Check possible update for this to RFC 5652 level
  */
 public class SignedData
     extends ASN1Object
@@ -35,6 +73,19 @@
     private boolean certsBer;
     private boolean        crlsBer;
 
+    /**
+     * Return a SignedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link SignedData} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignedData structure inside
+     * </ul>
+     *
+     * @param o the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static SignedData getInstance(
         Object  o)
     {
@@ -68,24 +119,6 @@
     }
 
 
-    // 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,
@@ -257,16 +290,6 @@
 
     /**
      * 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()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java
index 6e23b29..df22b8e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignedDataParser.java
@@ -11,6 +11,8 @@
 import org.bouncycastle.asn1.BERTags;
 
 /**
+ * Parser for <a href="http://tools.ietf.org/html/rfc5652#section-5.1">RFC 5652</a>: {@link SignedData} object.
+ * <p>
  * <pre>
  * SignedData ::= SEQUENCE {
  *     version CMSVersion,
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
index 37b6b31..2543eb1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerIdentifier.java
@@ -8,6 +8,21 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERTaggedObject;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-5.3">RFC 5652</a>:
+ * Identify who signed the containing {@link SignerInfo} object.
+ * <p>
+ * The certificates referred to by this are at containing {@link SignedData} structure.
+ * <p>
+ * <pre>
+ * SignerIdentifier ::= CHOICE {
+ *     issuerAndSerialNumber IssuerAndSerialNumber,
+ *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
+ * }
+ *
+ * SubjectKeyIdentifier ::= OCTET STRING
+ * </pre>
+ */
 public class SignerIdentifier
     extends ASN1Object
     implements ASN1Choice
@@ -33,7 +48,16 @@
     }
     
     /**
-     * return a SignerIdentifier object from the given object.
+     * Return a SignerIdentifier object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link SignerIdentifier} object
+     * <li> {@link IssuerAndSerialNumber} object
+     * <li> {@link org.bouncycastle.asn1.ASN1OctetString#getInstance(java.lang.Object) ASN1OctetString} input formats with SignerIdentifier structure inside
+     * <li> {@link org.bouncycastle.asn1.ASN1Primitive ASN1Primitive} for SignerIdentifier constructor.
+     * </ul>
      *
      * @param o the object we want converted.
      * @exception IllegalArgumentException if the object cannot be converted.
@@ -82,14 +106,6 @@
 
     /** 
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * SignerIdentifier ::= CHOICE {
-     *     issuerAndSerialNumber IssuerAndSerialNumber,
-     *     subjectKeyIdentifier [0] SubjectKeyIdentifier 
-     * }
-     *
-     * SubjectKeyIdentifier ::= OCTET STRING
-     * </pre>
      */
     public ASN1Primitive 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
index 8aafd67..4209045 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/SignerInfo.java
@@ -15,6 +15,63 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-5.3">RFC 5652</a>:
+ * Signature container per Signer, see {@link SignerIdentifier}.
+ * <pre>
+ * PKCS#7:
+ *
+ * SignerInfo ::= SEQUENCE {
+ *     version                   Version,
+ *     sid                       SignerIdentifier,
+ *     digestAlgorithm           DigestAlgorithmIdentifier,
+ *     authenticatedAttributes   [0] IMPLICIT Attributes OPTIONAL,
+ *     digestEncryptionAlgorithm DigestEncryptionAlgorithmIdentifier,
+ *     encryptedDigest           EncryptedDigest,
+ *     unauthenticatedAttributes [1] IMPLICIT Attributes OPTIONAL
+ * }
+ *
+ * EncryptedDigest ::= OCTET STRING
+ *
+ * DigestAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * DigestEncryptionAlgorithmIdentifier ::= AlgorithmIdentifier
+ *
+ * -----------------------------------------
+ *
+ * RFC 5256:
+ *
+ * SignerInfo ::= SEQUENCE {
+ *     version            CMSVersion,
+ *     sid                SignerIdentifier,
+ *     digestAlgorithm    DigestAlgorithmIdentifier,
+ *     signedAttrs        [0] IMPLICIT SignedAttributes OPTIONAL,
+ *     signatureAlgorithm SignatureAlgorithmIdentifier,
+ *     signature          SignatureValue,
+ *     unsignedAttrs      [1] IMPLICIT UnsignedAttributes OPTIONAL
+ * }
+ *
+ * -- {@link SignerIdentifier} referenced certificates are at containing
+ * -- {@link SignedData} certificates element.
+ *
+ * SignerIdentifier ::= CHOICE {
+ *     issuerAndSerialNumber {@link IssuerAndSerialNumber},
+ *     subjectKeyIdentifier  [0] SubjectKeyIdentifier }
+ *
+ * -- See {@link Attributes} for generalized SET OF {@link Attribute}
+ *
+ * SignedAttributes   ::= SET SIZE (1..MAX) OF Attribute
+ * UnsignedAttributes ::= SET SIZE (1..MAX) OF Attribute
+ * 
+ * {@link Attribute} ::= SEQUENCE {
+ *     attrType   OBJECT IDENTIFIER,
+ *     attrValues SET OF AttributeValue }
+ *
+ * AttributeValue ::= ANY
+ * 
+ * SignatureValue ::= OCTET STRING
+ * </pre>
+ */
 public class SignerInfo
     extends ASN1Object
 {
@@ -26,22 +83,44 @@
     private ASN1OctetString         encryptedDigest;
     private ASN1Set                 unauthenticatedAttributes;
 
+    /**
+     * Return a SignerInfo object from the given input
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link SignerInfo} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with SignerInfo structure inside
+     * </ul>
+     *
+     * @param o the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static SignerInfo getInstance(
         Object  o)
         throws IllegalArgumentException
     {
-        if (o == null || o instanceof SignerInfo)
+        if (o instanceof SignerInfo)
         {
             return (SignerInfo)o;
         }
-        else if (o instanceof ASN1Sequence)
+        else if (o != null)
         {
-            return new SignerInfo((ASN1Sequence)o);
+            return new SignerInfo(ASN1Sequence.getInstance(o));
         }
 
-        throw new IllegalArgumentException("unknown object in factory: " + o.getClass().getName());
+        return null;
     }
 
+    /**
+     *
+     * @param sid
+     * @param digAlgorithm            CMS knows as 'digestAlgorithm'
+     * @param authenticatedAttributes CMS knows as 'signedAttrs'
+     * @param digEncryptionAlgorithm  CMS knows as 'signatureAlgorithm'
+     * @param encryptedDigest         CMS knows as 'signature'
+     * @param unauthenticatedAttributes CMS knows as 'unsignedAttrs'
+     */
     public SignerInfo(
         SignerIdentifier        sid,
         AlgorithmIdentifier     digAlgorithm,
@@ -67,6 +146,15 @@
         this.unauthenticatedAttributes = unauthenticatedAttributes;
     }
 
+    /**
+     *
+     * @param sid
+     * @param digAlgorithm            CMS knows as 'digestAlgorithm'
+     * @param authenticatedAttributes CMS knows as 'signedAttrs'
+     * @param digEncryptionAlgorithm  CMS knows as 'signatureAlgorithm'
+     * @param encryptedDigest         CMS knows as 'signature'
+     * @param unauthenticatedAttributes CMS knows as 'unsignedAttrs'
+     */
     public SignerInfo(
         SignerIdentifier        sid,
         AlgorithmIdentifier     digAlgorithm,
@@ -167,23 +255,6 @@
 
     /**
      * 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()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java
index 2087248..977fce6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/Time.java
@@ -12,6 +12,22 @@
 import org.bouncycastle.asn1.DERGeneralizedTime;
 import org.bouncycastle.asn1.DERUTCTime;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5652#section-11.3">RFC 5652</a>:
+ * Dual-mode timestamp format producing either UTCTIme or GeneralizedTime.
+ * <p>
+ * <pre>
+ * Time ::= CHOICE {
+ *     utcTime        UTCTime,
+ *     generalTime    GeneralizedTime }
+ * </pre>
+ * <p>
+ * This has a constructor using java.util.Date for input which generates
+ * a {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object if the
+ * supplied datetime is in range 1950-01-01-00:00:00 UTC until 2049-12-31-23:59:60 UTC.
+ * If the datetime value is outside that range, the generated object will be
+ * {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime}.
+ */
 public class Time
     extends ASN1Object
     implements ASN1Choice
@@ -25,6 +41,9 @@
         return getInstance(obj.getObject());
     }
 
+    /**
+     * @deprecated use getInstance()
+     */
     public Time(
         ASN1Primitive   time)
     {
@@ -38,7 +57,7 @@
     }
 
     /**
-     * creates a time object from a given date - if the date is between 1950
+     * Create a time object from a given date - if the year is in between 1950
      * and 2049 a UTCTime object is generated, otherwise a GeneralizedTime
      * is used.
      */
@@ -63,6 +82,20 @@
         }
     }
 
+    /**
+     * Return a Time object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link Time} object
+     * <li> {@link org.bouncycastle.asn1.DERUTCTime DERUTCTime} object
+     * <li> {@link org.bouncycastle.asn1.DERGeneralizedTime DERGeneralizedTime} object
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static Time getInstance(
         Object  obj)
     {
@@ -82,6 +115,9 @@
         throw new IllegalArgumentException("unknown object in factory: " + obj.getClass().getName());
     }
 
+    /**
+     * Get the date+tine as a String in full form century format.
+     */
     public String getTime()
     {
         if (time instanceof DERUTCTime)
@@ -94,6 +130,9 @@
         }
     }
 
+    /**
+     * Get java.util.Date version of date+time.
+     */
     public Date getDate()
     {
         try
@@ -115,11 +154,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * Time ::= CHOICE {
-     *             utcTime        UTCTime,
-     *             generalTime    GeneralizedTime }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java
index ee1044f..f6d8d5a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampAndCRL.java
@@ -7,6 +7,16 @@
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.x509.CertificateList;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>
+ * Binding Documents with Time-Stamps; TimeStampAndCRL object.
+ * <pre>
+ * TimeStampAndCRL ::= SEQUENCE {
+ *     timeStamp   TimeStampToken,          -- according to RFC 3161
+ *     crl         CertificateList OPTIONAL -- according to RFC 5280
+ *  }
+ * </pre>
+ */
 public class TimeStampAndCRL
     extends ASN1Object
 {
@@ -27,6 +37,19 @@
         }
     }
 
+    /**
+     * Return a TimeStampAndCRL object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link TimeStampAndCRL} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with TimeStampAndCRL structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static TimeStampAndCRL getInstance(Object obj)
     {
         if (obj instanceof TimeStampAndCRL)
@@ -57,15 +80,6 @@
         return this.crl;
     }
 
-    /**
-     * <pre>
-     * TimeStampAndCRL ::= SEQUENCE {
-     *     timeStamp   TimeStampToken,          -- according to RFC 3161
-     *     crl         CertificateList OPTIONAL -- according to RFC 5280
-     *  }
-     * </pre>
-     * @return
-     */
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
index 6adefbb..5461147 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampTokenEvidence.java
@@ -9,6 +9,14 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>
+ * Binding Documents with Time-Stamps; TimeStampTokenEvidence object.
+ * <pre>
+ * TimeStampTokenEvidence ::=
+ *    SEQUENCE SIZE(1..MAX) OF TimeStampAndCRL
+ * </pre>
+ */
 public class TimeStampTokenEvidence
     extends ASN1Object
 {
@@ -43,6 +51,19 @@
         return getInstance(ASN1Sequence.getInstance(tagged, explicit));
     }
 
+    /**
+     * Return a TimeStampTokenEvidence object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link TimeStampTokenEvidence} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with TimeStampTokenEvidence structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static TimeStampTokenEvidence getInstance(Object obj)
     {
         if (obj instanceof TimeStampTokenEvidence)
@@ -62,13 +83,6 @@
         return timeStampAndCRLs;
     }
     
-    /**
-     * <pre>
-     * TimeStampTokenEvidence ::=
-     *    SEQUENCE SIZE(1..MAX) OF TimeStampAndCRL
-     * </pre>
-     * @return
-     */
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java
index ca8b696..f19061e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedData.java
@@ -9,6 +9,20 @@
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.DERIA5String;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>:
+ * Binding Documents with Time-Stamps; TimeStampedData object.
+ * <p>
+ * <pre>
+ * TimeStampedData ::= SEQUENCE {
+ *   version              INTEGER { v1(1) },
+ *   dataUri              IA5String OPTIONAL,
+ *   metaData             MetaData OPTIONAL,
+ *   content              OCTET STRING OPTIONAL,
+ *   temporalEvidence     Evidence
+ * }
+ * </pre>
+ */
 public class TimeStampedData
     extends ASN1Object
 {
@@ -47,18 +61,26 @@
         this.temporalEvidence = Evidence.getInstance(seq.getObjectAt(index));
     }
 
+    /**
+     * Return a TimeStampedData object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link RecipientKeyIdentifier} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence#getInstance(java.lang.Object) ASN1Sequence} input formats with TimeStampedData structure inside
+     * </ul>
+     *
+     * @param obj the object we want converted.
+     * @exception IllegalArgumentException if the object cannot be converted.
+     */
     public static TimeStampedData getInstance(Object obj)
     {
-        if (obj instanceof TimeStampedData)
+        if (obj == null || obj instanceof TimeStampedData)
         {
             return (TimeStampedData)obj;
         }
-        else if (obj != null)
-        {
-            return new TimeStampedData(ASN1Sequence.getInstance(obj));
-        }
-
-        return null;
+        return new TimeStampedData(ASN1Sequence.getInstance(obj));
     }
 
     public DERIA5String getDataUri()
@@ -81,18 +103,6 @@
         return temporalEvidence;
     }
 
-    /**
-     * <pre>
-     * TimeStampedData ::= SEQUENCE {
-     *   version              INTEGER { v1(1) },
-     *   dataUri              IA5String OPTIONAL,
-     *   metaData             MetaData OPTIONAL,
-     *   content              OCTET STRING OPTIONAL,
-     *   temporalEvidence     Evidence
-     * }
-     * </pre>
-     * @return
-     */
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java
index 0d050eb..f53e00f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/TimeStampedDataParser.java
@@ -12,6 +12,20 @@
 import org.bouncycastle.asn1.BERSequence;
 import org.bouncycastle.asn1.DERIA5String;
 
+/**
+ * Parser for <a href="http://tools.ietf.org/html/rfc5544">RFC 5544</a>:
+ * {@link TimeStampedData} object.
+ * <p>
+ * <pre>
+ * TimeStampedData ::= SEQUENCE {
+ *   version              INTEGER { v1(1) },
+ *   dataUri              IA5String OPTIONAL,
+ *   metaData             MetaData OPTIONAL,
+ *   content              OCTET STRING OPTIONAL,
+ *   temporalEvidence     Evidence
+ * }
+ * </pre>
+ */
 public class TimeStampedDataParser
 {
     private ASN1Integer version;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java
index 7beb6a4..bd7267b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cms/ecc/MQVuserKeyingMaterial.java
@@ -10,6 +10,14 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.cms.OriginatorPublicKey;
 
+/**
+ * <a href="http://tools.ietf.org/html/rfc5753">RFC 5753/3278</a>: MQVuserKeyingMaterial object.
+ * <pre>
+ * MQVuserKeyingMaterial ::= SEQUENCE {
+ *   ephemeralPublicKey OriginatorPublicKey,
+ *   addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL  }
+ * </pre>
+ */
 public class MQVuserKeyingMaterial
     extends ASN1Object
 {
@@ -42,7 +50,7 @@
     }
 
     /**
-     * return an MQVuserKeyingMaterial object from a tagged object.
+     * Return an MQVuserKeyingMaterial object from a tagged object.
      *
      * @param obj      the tagged object holding the object we want.
      * @param explicit true if the object is meant to be explicitly
@@ -58,7 +66,14 @@
     }
 
     /**
-     * return an MQVuserKeyingMaterial object from the given object.
+     * Return an MQVuserKeyingMaterial object from the given object.
+     * <p>
+     * Accepted inputs:
+     * <ul>
+     * <li> null &rarr; null
+     * <li> {@link MQVuserKeyingMaterial} object
+     * <li> {@link org.bouncycastle.asn1.ASN1Sequence ASN1Sequence} with MQVuserKeyingMaterial inside it.
+     * </ul>
      *
      * @param obj the object we want converted.
      * @throws IllegalArgumentException if the object cannot be converted.
@@ -91,11 +106,6 @@
 
     /**
      * Produce an object suitable for an ASN1OutputStream.
-     * <pre>
-     * MQVuserKeyingMaterial ::= SEQUENCE {
-     *   ephemeralPublicKey OriginatorPublicKey,
-     *   addedukm [0] EXPLICIT UserKeyingMaterial OPTIONAL  }
-     * </pre>
      */
     public ASN1Primitive toASN1Primitive()
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cms/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cms/package.html
deleted file mode 100644
index c165a7a..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cms/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting Cryptographic Message Syntax as described in PKCS#7 and RFC 3369 (formerly RFC 2630).
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
index c36084d..c298a7e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/crmf/CRMFObjectIdentifiers.java
@@ -5,17 +5,25 @@
 
 public interface CRMFObjectIdentifiers
 {
+    /** 1.3.6.1.5.5.7 */
     static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
 
     // arc for Internet X.509 PKI protocols and their components
 
-    static final ASN1ObjectIdentifier id_pkip  = id_pkix.branch("5");
+    /** 1.3.6.1.5.5.7.5 */
+    static final ASN1ObjectIdentifier id_pkip    = id_pkix.branch("5");
 
+    /** 1.3.6.1.5.5.7.1 */
     static final ASN1ObjectIdentifier id_regCtrl = id_pkip.branch("1");
-    static final ASN1ObjectIdentifier id_regCtrl_regToken = id_regCtrl.branch("1");
-    static final ASN1ObjectIdentifier id_regCtrl_authenticator = id_regCtrl.branch("2");
+    /** 1.3.6.1.5.5.7.1.1 */
+    static final ASN1ObjectIdentifier id_regCtrl_regToken           = id_regCtrl.branch("1");
+    /** 1.3.6.1.5.5.7.1.2 */
+    static final ASN1ObjectIdentifier id_regCtrl_authenticator      = id_regCtrl.branch("2");
+    /** 1.3.6.1.5.5.7.1.3 */
     static final ASN1ObjectIdentifier id_regCtrl_pkiPublicationInfo = id_regCtrl.branch("3");
-    static final ASN1ObjectIdentifier id_regCtrl_pkiArchiveOptions = id_regCtrl.branch("4");
+    /** 1.3.6.1.5.5.7.1.4 */
+    static final ASN1ObjectIdentifier id_regCtrl_pkiArchiveOptions  = id_regCtrl.branch("4");
 
-    static final ASN1ObjectIdentifier id_ct_encKeyWithID = new ASN1ObjectIdentifier(PKCSObjectIdentifiers.id_ct + ".21");
+    /** 1.2.840.113549.1.9.16.1,21 */
+    static final ASN1ObjectIdentifier id_ct_encKeyWithID = PKCSObjectIdentifiers.id_ct.branch("21");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
index fb5ae79..c6e8e0d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/CryptoProObjectIdentifiers.java
@@ -2,47 +2,100 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * <pre>
+ * GOST Algorithms OBJECT IDENTIFIERS :
+ *    { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)}
+ * </pre>
+ */
 public interface CryptoProObjectIdentifiers
 {
-    // GOST Algorithms OBJECT IDENTIFIERS :
-    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2)}
-    static final ASN1ObjectIdentifier    GOST_id              = new ASN1ObjectIdentifier("1.2.643.2.2");
+    /** Base OID: 1.2.643.2.2 */
+    static final ASN1ObjectIdentifier    GOST_id            = new ASN1ObjectIdentifier("1.2.643.2.2");
 
+    /** Gost R3411 OID: 1.2.643.2.2.9 */
     static final ASN1ObjectIdentifier    gostR3411          = GOST_id.branch("9");
+    /** Gost R3411 HMAC OID: 1.2.643.2.2.10 */
     static final ASN1ObjectIdentifier    gostR3411Hmac      = GOST_id.branch("10");
 
-    static final ASN1ObjectIdentifier    gostR28147_cbc     = new ASN1ObjectIdentifier(GOST_id+".21");
+    /** Gost R28147 OID: 1.2.643.2.2.21 */
+    static final ASN1ObjectIdentifier    gostR28147_gcfb = GOST_id.branch("21");
 
+    /** Gost R28147-89-CryotoPro-A-ParamSet OID: 1.2.643.2.2.31.1 */
     static final ASN1ObjectIdentifier    id_Gost28147_89_CryptoPro_A_ParamSet = GOST_id.branch("31.1");
 
-    static final ASN1ObjectIdentifier    gostR3410_94       = new ASN1ObjectIdentifier(GOST_id+".20");
-    static final ASN1ObjectIdentifier    gostR3410_2001     = new ASN1ObjectIdentifier(GOST_id+".19");
-    static final ASN1ObjectIdentifier    gostR3411_94_with_gostR3410_94   = new ASN1ObjectIdentifier(GOST_id+".4");
-    static final ASN1ObjectIdentifier    gostR3411_94_with_gostR3410_2001 = new ASN1ObjectIdentifier(GOST_id+".3");
+    /** Gost R28147-89-CryotoPro-B-ParamSet OID: 1.2.643.2.2.31.2 */
+    static final ASN1ObjectIdentifier    id_Gost28147_89_CryptoPro_B_ParamSet = GOST_id.branch("31.2");
 
-    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) }
-    static final ASN1ObjectIdentifier    gostR3411_94_CryptoProParamSet = new ASN1ObjectIdentifier(GOST_id+".30.1");
+    /** Gost R28147-89-CryotoPro-C-ParamSet OID: 1.2.643.2.2.31.3 */
+    static final ASN1ObjectIdentifier    id_Gost28147_89_CryptoPro_C_ParamSet = GOST_id.branch("31.3");
 
-    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) }
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_A     = new ASN1ObjectIdentifier(GOST_id+".32.2");
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_B     = new ASN1ObjectIdentifier(GOST_id+".32.3");
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_C     = new ASN1ObjectIdentifier(GOST_id+".32.4");
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_D     = new ASN1ObjectIdentifier(GOST_id+".32.5");
+    /** Gost R28147-89-CryotoPro-D-ParamSet OID: 1.2.643.2.2.31.4 */
+    static final ASN1ObjectIdentifier    id_Gost28147_89_CryptoPro_D_ParamSet = GOST_id.branch("31.4");
 
-    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) }
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_XchA  = new ASN1ObjectIdentifier(GOST_id+".33.1");
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_XchB  = new ASN1ObjectIdentifier(GOST_id+".33.2");
-    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_XchC  = new ASN1ObjectIdentifier(GOST_id+".33.3");
+    /** Gost R3410-94 OID: 1.2.643.2.2.20 */
+    static final ASN1ObjectIdentifier    gostR3410_94       = GOST_id.branch("20");
+    /** Gost R3410-2001 OID: 1.2.643.2.2.19 */
+    static final ASN1ObjectIdentifier    gostR3410_2001     = GOST_id.branch("19");
 
-    //{ iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) }
-    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_A = new ASN1ObjectIdentifier(GOST_id+".35.1");
-    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_B = new ASN1ObjectIdentifier(GOST_id+".35.2");
-    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_C = new ASN1ObjectIdentifier(GOST_id+".35.3");
+    /** Gost R3411-94-with-R3410-94 OID: 1.2.643.2.2.4 */
+    static final ASN1ObjectIdentifier    gostR3411_94_with_gostR3410_94   = GOST_id.branch("4");
+    /** Gost R3411-94-with-R3410-2001 OID: 1.2.643.2.2.3 */
+    static final ASN1ObjectIdentifier    gostR3411_94_with_gostR3410_2001 = GOST_id.branch("3");
 
-    // { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) }
-    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_XchA  = new ASN1ObjectIdentifier(GOST_id+".36.0");
-    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_XchB  = new ASN1ObjectIdentifier(GOST_id+".36.1");
+    /**
+     * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) hashes(30) }
+     * <p>
+     * Gost R3411-94-CryptoProParamSet OID: 1.2.643.2.2.30.1
+     */
+    static final ASN1ObjectIdentifier    gostR3411_94_CryptoProParamSet = GOST_id.branch("30.1");
+
+    /**
+     * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) signs(32) }
+     * <p>
+     * Gost R3410-94-CryptoPro-A OID: 1.2.643.2.2.32.2
+     */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_A     = GOST_id.branch("32.2");
+    /** Gost R3410-94-CryptoPro-B OID: 1.2.643.2.2.32.3 */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_B     = GOST_id.branch("32.3");
+    /** Gost R3410-94-CryptoPro-C OID: 1.2.643.2.2.32.4 */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_C     = GOST_id.branch("32.4");
+    /** Gost R3410-94-CryptoPro-D OID: 1.2.643.2.2.32.5 */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_D     = GOST_id.branch("32.5");
+
+    /**
+     * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) exchanges(33) }
+     * <p>
+     * Gost R3410-94-CryptoPro-XchA OID: 1.2.643.2.2.33.1
+     */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_XchA  = GOST_id.branch("33.1");
+    /** Gost R3410-94-CryptoPro-XchB OID: 1.2.643.2.2.33.2 */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_XchB  = GOST_id.branch("33.2");
+    /** Gost R3410-94-CryptoPro-XchC OID: 1.2.643.2.2.33.3 */
+    static final ASN1ObjectIdentifier    gostR3410_94_CryptoPro_XchC  = GOST_id.branch("33.3");
+
+    /**
+     * { iso(1) member-body(2)ru(643) rans(2) cryptopro(2) ecc-signs(35) }
+     * <p>
+     * Gost R3410-2001-CryptoPro-A OID: 1.2.643.2.2.35.1
+     */
+    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_A = GOST_id.branch("35.1");
+    /** Gost R3410-2001-CryptoPro-B OID: 1.2.643.2.2.35.2 */
+    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_B = GOST_id.branch("35.2");
+    /** Gost R3410-2001-CryptoPro-C OID: 1.2.643.2.2.35.3 */
+    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_C = GOST_id.branch("35.3");
+
+    /**
+     * { iso(1) member-body(2) ru(643) rans(2) cryptopro(2) ecc-exchanges(36) }
+     * <p>
+     * Gost R3410-2001-CryptoPro-XchA OID: 1.2.643.2.2.36.0
+     */
+    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_XchA  = GOST_id.branch("36.0");
+    /** Gost R3410-2001-CryptoPro-XchA OID: 1.2.643.2.2.36.1 */
+    static final ASN1ObjectIdentifier    gostR3410_2001_CryptoPro_XchB  = GOST_id.branch("36.1");
     
-    static final ASN1ObjectIdentifier    gost_ElSgDH3410_default    = new ASN1ObjectIdentifier(GOST_id+".36.0");
-    static final ASN1ObjectIdentifier    gost_ElSgDH3410_1          = new ASN1ObjectIdentifier(GOST_id+".36.1");
+    /** Gost R3410-ElSqDH3410-default OID: 1.2.643.2.2.36.0 */
+    static final ASN1ObjectIdentifier    gost_ElSgDH3410_default    = GOST_id.branch("36.0");
+    /** Gost R3410-ElSqDH3410-1 OID: 1.2.643.2.2.36.1 */
+    static final ASN1ObjectIdentifier    gost_ElSgDH3410_1          = GOST_id.branch("36.1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
index e203505..fb1d9e9 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/ECGOST3410NamedCurves.java
@@ -7,7 +7,6 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.math.ec.ECFieldElement;
 import org.bouncycastle.math.ec.ECPoint;
 
 /**
@@ -23,7 +22,7 @@
     {
         BigInteger mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319");
         BigInteger mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323");
-        
+
         ECCurve.Fp curve = new ECCurve.Fp(
             mod_p, // p
             new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"), // a
@@ -31,33 +30,33 @@
 
         ECDomainParameters ecParams = new ECDomainParameters(
             curve,
-            new ECPoint.Fp(curve,
-                    new ECFieldElement.Fp(curve.getQ(),new BigInteger("1")), // x
-                    new ECFieldElement.Fp(curve.getQ(),new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"))), // y
+            curve.createPoint(
+                new BigInteger("1"), // x
+                new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
             mod_q);
-        
+
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, ecParams);  
-        
+
         mod_p = new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639319");
         mod_q = new BigInteger("115792089237316195423570985008687907853073762908499243225378155805079068850323");
-        
+
         curve = new ECCurve.Fp(
-                mod_p, // p
-                new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"),
-                new BigInteger("166"));
+            mod_p, // p
+            new BigInteger("115792089237316195423570985008687907853269984665640564039457584007913129639316"),
+            new BigInteger("166"));
 
         ecParams = new ECDomainParameters(
-                curve,
-                new ECPoint.Fp(curve,
-                        new ECFieldElement.Fp(curve.getQ(),new BigInteger("1")), // x
-                        new ECFieldElement.Fp(curve.getQ(),new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612"))), // y
-                mod_q);
+            curve,
+            curve.createPoint(
+                new BigInteger("1"), // x
+                new BigInteger("64033881142927202683649881450433473985931760268884941288852745803908878638612")), // y
+            mod_q);
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA, ecParams); 
-        
+
         mod_p = new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823193"); //p
         mod_q = new BigInteger("57896044618658097711785492504343953927102133160255826820068844496087732066703"); //q
-        
+
         curve = new ECCurve.Fp(
             mod_p, // p
             new BigInteger("57896044618658097711785492504343953926634992332820282019728792003956564823190"), // a
@@ -65,30 +64,30 @@
 
         ecParams = new ECDomainParameters(
             curve,
-            new ECPoint.Fp(curve,
-                           new ECFieldElement.Fp(mod_p,new BigInteger("1")), // x
-                           new ECFieldElement.Fp(mod_p,new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124"))), // y
+            curve.createPoint(
+                new BigInteger("1"), // x
+                new BigInteger("28792665814854611296992347458380284135028636778229113005756334730996303888124")), // y
             mod_q); // q
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, ecParams);  
-        
+
         mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619");
         mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601");
-        
+
         curve = new ECCurve.Fp(
-                mod_p, // p
-                new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"),
-                new BigInteger("32858"));
+            mod_p, // p
+            new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502616"),
+            new BigInteger("32858"));
 
         ecParams = new ECDomainParameters(
-                curve,
-                new ECPoint.Fp(curve,
-                               new ECFieldElement.Fp(mod_p,new BigInteger("0")),
-                               new ECFieldElement.Fp(mod_p,new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"))),
+            curve,
+            curve.createPoint(
+                new BigInteger("0"),
+                new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")),
             mod_q);
-        
+
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB, ecParams);  
-                                
+
         mod_p = new BigInteger("70390085352083305199547718019018437841079516630045180471284346843705633502619"); //p
         mod_q = new BigInteger("70390085352083305199547718019018437840920882647164081035322601458352298396601"); //q
         curve = new ECCurve.Fp(
@@ -98,19 +97,19 @@
 
         ecParams = new ECDomainParameters(
             curve,
-            new ECPoint.Fp(curve,
-                           new ECFieldElement.Fp(mod_p,new BigInteger("0")), // x
-                           new ECFieldElement.Fp(mod_p,new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247"))), // y
+            curve.createPoint(
+                new BigInteger("0"), // x
+                new BigInteger("29818893917731240733471273240314769927240550812383695689146495261604565990247")), // y
             mod_q); // q
 
         params.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, ecParams); 
-            
+
         objIds.put("GostR3410-2001-CryptoPro-A", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A);
         objIds.put("GostR3410-2001-CryptoPro-B", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B);
         objIds.put("GostR3410-2001-CryptoPro-C", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C);
         objIds.put("GostR3410-2001-CryptoPro-XchA", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchA);
         objIds.put("GostR3410-2001-CryptoPro-XchB", CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_XchB);
-        
+
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_A, "GostR3410-2001-CryptoPro-A");
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_B, "GostR3410-2001-CryptoPro-B");
         names.put(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_C, "GostR3410-2001-CryptoPro-C");
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
index a0459c1..124bca6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST28147Parameters.java
@@ -11,11 +11,14 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERSequence;
 
+/**
+ * ASN.1 algorithm identifier parameters for GOST-28147
+ */
 public class GOST28147Parameters
     extends ASN1Object
 {
-    ASN1OctetString iv;
-    ASN1ObjectIdentifier paramSet;
+    private ASN1OctetString iv;
+    private ASN1ObjectIdentifier paramSet;
 
     public static GOST28147Parameters getInstance(
         ASN1TaggedObject obj,
@@ -27,19 +30,22 @@
     public static GOST28147Parameters getInstance(
         Object obj)
     {
-        if(obj == null || obj instanceof GOST28147Parameters)
+        if (obj instanceof GOST28147Parameters)
         {
             return (GOST28147Parameters)obj;
         }
 
-        if(obj instanceof ASN1Sequence)
+        if (obj != null)
         {
-            return new GOST28147Parameters((ASN1Sequence)obj);
+            return new GOST28147Parameters(ASN1Sequence.getInstance(obj));
         }
 
-        throw new IllegalArgumentException("Invalid GOST3410Parameter: " + obj.getClass().getName());
+        return null;
     }
 
+    /**
+     * @deprecated use the getInstance() method. This constructor will vanish!
+     */
     public GOST28147Parameters(
         ASN1Sequence  seq)
     {
@@ -69,4 +75,24 @@
 
         return new DERSequence(v);
     }
+
+    /**
+     * Return the OID representing the sBox to use.
+     *
+     * @return the sBox OID.
+     */
+    public ASN1ObjectIdentifier getEncryptionParamSet()
+    {
+        return paramSet;
+    }
+
+    /**
+     * Return the initialisation vector to use.
+     *
+     * @return the IV.
+     */
+    public byte[] getIV()
+    {
+        return iv.getOctets();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java
index 0307f50..45d7814 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/GOST3410PublicKeyAlgParameters.java
@@ -57,6 +57,9 @@
         this.encryptionParamSet = encryptionParamSet;
     }
 
+    /**
+     * @deprecated use getInstance()
+     */
     public GOST3410PublicKeyAlgParameters(
         ASN1Sequence  seq)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/package.html
deleted file mode 100644
index 2b0af9e..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/cryptopro/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes for CRYPTO-PRO related objects - such as GOST identifiers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java
index 1a88c34..d5f6ab6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/DVCSObjectIdentifiers.java
@@ -2,35 +2,28 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * OIDs for <a href="http://tools.ietf.org/html/rfc3029">RFC 3029</a>
+ * Data Validation and Certification Server Protocols
+ */
 public interface DVCSObjectIdentifiers
 {
+    /** Base OID id-pkix: 1.3.6.1.5.5.7 */
+    static final ASN1ObjectIdentifier id_pkix  = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
+    /** Base OID id-smime: 1.2.840.113549.1.9.16 */
+    static final ASN1ObjectIdentifier id_smime = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16");
 
-    //    id-pkix     OBJECT IDENTIFIER ::= {iso(1)
-    //                   identified-organization(3) dod(6)
-    //                   internet(1) security(5) mechanisms(5) pkix(7)}
-    //
-    //    id-smime    OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-    //                   us(840) rsadsi(113549) pkcs(1) pkcs-9(9) 16 }
-    public static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
-    public static final ASN1ObjectIdentifier id_smime = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16");
+    /** Authority Information Access for DVCS; id-ad-dcvs;  OID: 1.3.6.1.5.5.7.48.4 */
+    static final ASN1ObjectIdentifier id_ad_dvcs = id_pkix.branch("48.4");
 
-    //    -- Authority Information Access for DVCS
-    //
-    //    id-ad-dvcs  OBJECT IDENTIFIER ::= {id-pkix id-ad(48) 4}
-    public static final ASN1ObjectIdentifier id_ad_dvcs = id_pkix.branch("48.4");
+    /** Key Purpose for DVCS; id-kp-dvcs; OID: 1.3.6.1.5.5.7.3.10 */
+    static final ASN1ObjectIdentifier id_kp_dvcs = id_pkix.branch("3.10");
 
-    //    -- Key Purpose for DVCS
-    //
-    //    id-kp-dvcs  OBJECT IDENTIFIER ::= {id-pkix id-kp(3) 10}
-    public static final ASN1ObjectIdentifier id_kp_dvcs = id_pkix.branch("3.10");
+    /** SMIME eContentType id-ct-DVCSRequestData;   OID: 1.2.840.113549.1.9.16.1.7 */
+    static final ASN1ObjectIdentifier id_ct_DVCSRequestData  = id_smime.branch("1.7");
+    /** SMIME eContentType id-ct-DVCSResponseData;  OID: 1.2.840.113549.1.9.16.1.8 */
+    static final ASN1ObjectIdentifier id_ct_DVCSResponseData = id_smime.branch("1.8");
 
-    //    id-ct-DVCSRequestData  OBJECT IDENTIFIER ::= { id-smime ct(1) 7 }
-    //    id-ct-DVCSResponseData OBJECT IDENTIFIER ::= { id-smime ct(1) 8 }
-    public static final ASN1ObjectIdentifier id_ct_DVCSRequestData = id_smime.branch("1.7");
-    public static final ASN1ObjectIdentifier id_ct_DVCSResponseData = id_smime.branch("1.8");
-
-    //    -- Data validation certificate attribute
-    //
-    //    id-aa-dvcs-dvc OBJECT IDENTIFIER ::= { id-smime aa(2) 29 }
-    public static final ASN1ObjectIdentifier id_aa_dvcs_dvc = id_smime.branch("2.29");
+    /** SMIME DataValidation certificate attribute id-aa-dvcs-dvc;  OID: 1.2.840.113549.1.9.16.2,29 */
+    static final ASN1ObjectIdentifier id_aa_dvcs_dvc = id_smime.branch("2.29");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/package.html
deleted file mode 100644
index a941922..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/dvcs/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and processing Data Validation and Certification Server (DVCS) protocols as described in RFC 3029.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
index bef8620..77416dc 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/eac/EACObjectIdentifiers.java
@@ -2,54 +2,109 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * German Federal Office for Information Security
+ * (Bundesamt für Sicherheit in der Informationstechnik)
+ * <a href="http://www.bsi.bund.de/">http://www.bsi.bund.de/</a>
+ * <p>
+ * <a href="https://www.bsi.bund.de/EN/Publications/TechnicalGuidelines/TR03110/BSITR03110.html">BSI TR-03110</a>
+ * Technical Guideline Advanced Security Mechanisms for Machine Readable Travel Documents
+ * <p>
+ * <a href="https://www.bsi.bund.de/SharedDocs/Downloads/EN/BSI/Publications/TechGuidelines/TR03110/TR-03110_v2.1_P3pdf.pdf?__blob=publicationFile">Technical Guideline TR-03110-3</a>
+ * Advanced Security Mechanisms for Machine Readable Travel Documents;
+ * Part 3: Common Specifications.
+ */
+
 public interface EACObjectIdentifiers
 {
-    // bsi-de OBJECT IDENTIFIER ::= {
-    //         itu-t(0) identified-organization(4) etsi(0)
-    //         reserved(127) etsi-identified-organization(0) 7
-    //     }
+    /**
+     * <pre>
+     * bsi-de OBJECT IDENTIFIER ::= {
+     *     itu-t(0) identified-organization(4) etsi(0)
+     *     reserved(127) etsi-identified-organization(0) 7
+     * }
+     * </pre>
+     * OID: 0.4.0.127.0.7
+     */
     static final ASN1ObjectIdentifier    bsi_de      = new ASN1ObjectIdentifier("0.4.0.127.0.7");
 
-    // id-PK OBJECT IDENTIFIER ::= {
-    //         bsi-de protocols(2) smartcard(2) 1
-    //     }
-    static final ASN1ObjectIdentifier    id_PK = bsi_de.branch("2.2.1");
+    /**
+     * <pre>
+     * id-PK OBJECT IDENTIFIER ::= {
+     *     bsi-de protocols(2) smartcard(2) 1
+     * }
+     * </pre>
+     * OID: 0.4.0.127.0.7.2.2.1
+     */
+    static final ASN1ObjectIdentifier    id_PK      = bsi_de.branch("2.2.1");
 
-    static final ASN1ObjectIdentifier    id_PK_DH = id_PK.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.1.1 */
+    static final ASN1ObjectIdentifier    id_PK_DH   = id_PK.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.1.2 */
     static final ASN1ObjectIdentifier    id_PK_ECDH = id_PK.branch("2");
 
-    // 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");
+    /**
+     * <pre>
+     * id-CA OBJECT IDENTIFIER ::= {
+     *     bsi-de protocols(2) smartcard(2) 3
+     * }
+     * </pre>
+     * OID: 0.4.0.127.0.7.2.2.3
+     */
+    static final ASN1ObjectIdentifier    id_CA                   = bsi_de.branch("2.2.3");
+    /** OID: 0.4.0.127.0.7.2.2.3.1 */
+    static final ASN1ObjectIdentifier    id_CA_DH                = id_CA.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.3.1.1 */
+    static final ASN1ObjectIdentifier    id_CA_DH_3DES_CBC_CBC   = id_CA_DH.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.3.2 */
+    static final ASN1ObjectIdentifier    id_CA_ECDH              = id_CA.branch("2");
+    /** OID: 0.4.0.127.0.7.2.2.3.2.1 */
     static final ASN1ObjectIdentifier    id_CA_ECDH_3DES_CBC_CBC = id_CA_ECDH.branch("1");
 
-    //
-    // id-TA OBJECT IDENTIFIER ::= {
-    //     bsi-de protocols(2) smartcard(2) 2
-    // }
+    /**
+     * <pre>
+     * id-TA OBJECT IDENTIFIER ::= {
+     *     bsi-de protocols(2) smartcard(2) 2
+     * }
+     * </pre>
+     * OID: 0.4.0.127.0.7.2.2.2
+     */
     static final ASN1ObjectIdentifier    id_TA = bsi_de.branch("2.2.2");
 
-    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");
+    /** OID: 0.4.0.127.0.7.2.2.2.1 */
+    static final ASN1ObjectIdentifier    id_TA_RSA              = id_TA.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.2.1.1 */
+    static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_1   = id_TA_RSA.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.2.1.2 */
     static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_256 = id_TA_RSA.branch("2");
-    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");
+    /** OID: 0.4.0.127.0.7.2.2.2.1.3 */
+    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_1    = id_TA_RSA.branch("3");
+    /** OID: 0.4.0.127.0.7.2.2.2.1.4 */
+    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_256  = id_TA_RSA.branch("4");
+    /** OID: 0.4.0.127.0.7.2.2.2.1.5 */
     static final ASN1ObjectIdentifier    id_TA_RSA_v1_5_SHA_512 = id_TA_RSA.branch("5");
-    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");
+    /** OID: 0.4.0.127.0.7.2.2.2.1.6 */
+    static final ASN1ObjectIdentifier    id_TA_RSA_PSS_SHA_512  = id_TA_RSA.branch("6");
+    /** OID: 0.4.0.127.0.7.2.2.2.2 */
+    static final ASN1ObjectIdentifier    id_TA_ECDSA            = id_TA.branch("2");
+    /** OID: 0.4.0.127.0.7.2.2.2.2.1 */
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_1      = id_TA_ECDSA.branch("1");
+    /** OID: 0.4.0.127.0.7.2.2.2.2.2 */
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_224    = id_TA_ECDSA.branch("2");
+    /** OID: 0.4.0.127.0.7.2.2.2.2.3 */
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_256    = id_TA_ECDSA.branch("3");
+    /** OID: 0.4.0.127.0.7.2.2.2.2.4 */
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_384    = id_TA_ECDSA.branch("4");
+    /** OID: 0.4.0.127.0.7.2.2.2.2.5 */
+    static final ASN1ObjectIdentifier    id_TA_ECDSA_SHA_512    = id_TA_ECDSA.branch("5");
 
     /**
+     * <pre>
      * id-EAC-ePassport OBJECT IDENTIFIER ::= {
-     * bsi-de applications(3) mrtd(1) roles(2) 1}
+     *     bsi-de applications(3) mrtd(1) roles(2) 1
+     * }
+     * </pre>
+     * OID: 0.4.0.127.0.7.3.1.2.1
      */
     static final ASN1ObjectIdentifier id_EAC_ePassport = bsi_de.branch("3.1.2.1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/esf/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/esf/package.html
deleted file mode 100644
index de27367..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/esf/package.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting [ESF] RFC3126 
-Electronic Signature Formats for long term electronic signatures.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ess/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/ess/package.html
deleted file mode 100644
index 21854b3..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ess/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting Enhanced Security Services for S/MIME as described RFC 2634 and RFC 5035.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
index 084a020..a329e63 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/gnu/GNUObjectIdentifiers.java
@@ -2,29 +2,57 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ *  GNU project OID collection<p>
+ *  { iso(1) identifier-organization(3) dod(6) internet(1) private(4) } == IETF defined things
+ */
 public interface GNUObjectIdentifiers
 {
-    public static final ASN1ObjectIdentifier GNU = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius
-    public static final ASN1ObjectIdentifier GnuPG = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten)
+    /** 1.3.6.1.4.1.11591.1 -- used by GNU Radius */
+    public static final ASN1ObjectIdentifier GNU      = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.1"); // GNU Radius
+    /** 1.3.6.1.4.1.11591.2 -- used by GNU PG */
+    public static final ASN1ObjectIdentifier GnuPG    = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2"); // GnuPG (Ägypten)
+    /** 1.3.6.1.4.1.11591.2.1 -- notation */
     public static final ASN1ObjectIdentifier notation = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1"); // notation
+    /** 1.3.6.1.4.1.11591.2.1.1 -- pkaAddress */
     public static final ASN1ObjectIdentifier pkaAddress = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.2.1.1"); // pkaAddress
+    /** 1.3.6.1.4.1.11591.3 -- GNU Radar */
     public static final ASN1ObjectIdentifier GnuRadar = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.3"); // GNU Radar
+    /** 1.3.6.1.4.1.11591.12 -- digestAlgorithm */
     public static final ASN1ObjectIdentifier digestAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12"); // digestAlgorithm
+    /** 1.3.6.1.4.1.11591.12.2 -- TIGER/192 */
     public static final ASN1ObjectIdentifier Tiger_192 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.12.2"); // TIGER/192
+    /** 1.3.6.1.4.1.11591.13 -- encryptionAlgorithm */
     public static final ASN1ObjectIdentifier encryptionAlgorithm = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13"); // encryptionAlgorithm
+    /** 1.3.6.1.4.1.11591.13.2 -- Serpent */
     public static final ASN1ObjectIdentifier Serpent = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2"); // Serpent
+    /** 1.3.6.1.4.1.11591.13.2.1 -- Serpent-128-ECB */
     public static final ASN1ObjectIdentifier Serpent_128_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.1"); // Serpent-128-ECB
+    /** 1.3.6.1.4.1.11591.13.2.2 -- Serpent-128-CBC */
     public static final ASN1ObjectIdentifier Serpent_128_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.2"); // Serpent-128-CBC
+    /** 1.3.6.1.4.1.11591.13.2.3 -- Serpent-128-OFB */
     public static final ASN1ObjectIdentifier Serpent_128_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.3"); // Serpent-128-OFB
+    /** 1.3.6.1.4.1.11591.13.2.4 -- Serpent-128-CFB */
     public static final ASN1ObjectIdentifier Serpent_128_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.4"); // Serpent-128-CFB
+    /** 1.3.6.1.4.1.11591.13.2.21 -- Serpent-192-ECB */
     public static final ASN1ObjectIdentifier Serpent_192_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.21"); // Serpent-192-ECB
+    /** 1.3.6.1.4.1.11591.13.2.22 -- Serpent-192-CCB */
     public static final ASN1ObjectIdentifier Serpent_192_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.22"); // Serpent-192-CBC
+    /** 1.3.6.1.4.1.11591.13.2.23 -- Serpent-192-OFB */
     public static final ASN1ObjectIdentifier Serpent_192_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.23"); // Serpent-192-OFB
+    /** 1.3.6.1.4.1.11591.13.2.24 -- Serpent-192-CFB */
     public static final ASN1ObjectIdentifier Serpent_192_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.24"); // Serpent-192-CFB
+    /** 1.3.6.1.4.1.11591.13.2.41 -- Serpent-256-ECB */
     public static final ASN1ObjectIdentifier Serpent_256_ECB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.41"); // Serpent-256-ECB
+    /** 1.3.6.1.4.1.11591.13.2.42 -- Serpent-256-CBC */
     public static final ASN1ObjectIdentifier Serpent_256_CBC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.42"); // Serpent-256-CBC
+    /** 1.3.6.1.4.1.11591.13.2.43 -- Serpent-256-OFB */
     public static final ASN1ObjectIdentifier Serpent_256_OFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.43"); // Serpent-256-OFB
+    /** 1.3.6.1.4.1.11591.13.2.44 -- Serpent-256-CFB */
     public static final ASN1ObjectIdentifier Serpent_256_CFB = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.13.2.44"); // Serpent-256-CFB
+
+    /** 1.3.6.1.4.1.11591.14 -- CRC algorithms */
     public static final ASN1ObjectIdentifier CRC = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14"); // CRC algorithms
+    /** 1.3.6.1.4.1.11591.14,1 -- CRC32 */
     public static final ASN1ObjectIdentifier CRC32 = new ASN1ObjectIdentifier("1.3.6.1.4.1.11591.14.1"); // CRC 32
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
index e9ab8d6..5bfdbab 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/iana/IANAObjectIdentifiers.java
@@ -2,19 +2,59 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * IANA:
+ *  { iso(1) identifier-organization(3) dod(6) internet(1) } == IETF defined things
+ */
 public interface IANAObjectIdentifiers
 {
+
+    /** { iso(1) identifier-organization(3) dod(6) internet(1) } == IETF defined things */
+    static final ASN1ObjectIdentifier   internet       = new ASN1ObjectIdentifier("1.3.6.1");
+    /** 1.3.6.1.1: Internet directory: X.500 */
+    static final ASN1ObjectIdentifier   directory      = internet.branch("1");
+    /** 1.3.6.1.2: Internet management */
+    static final ASN1ObjectIdentifier   mgmt           = internet.branch("2");
+    /** 1.3.6.1.3: */
+    static final ASN1ObjectIdentifier   experimental   = internet.branch("3");
+    /** 1.3.6.1.4: */
+    static final ASN1ObjectIdentifier   _private       = internet.branch("4");
+    /** 1.3.6.1.5: Security services */
+    static final ASN1ObjectIdentifier   security       = internet.branch("5");
+    /** 1.3.6.1.6: SNMPv2 -- never really used */
+    static final ASN1ObjectIdentifier   SNMPv2         = internet.branch("6");
+    /** 1.3.6.1.7: mail -- never really used */
+    static final ASN1ObjectIdentifier   mail           = internet.branch("7");
+
+
     // id-SHA1 OBJECT IDENTIFIER ::=    
     // {iso(1) identified-organization(3) dod(6) internet(1) security(5) mechanisms(5) ipsec(8) isakmpOakley(1)}
     //
 
-    static final ASN1ObjectIdentifier    isakmpOakley  = new ASN1ObjectIdentifier("1.3.6.1.5.5.8.1");
 
-    static final ASN1ObjectIdentifier    hmacMD5       = new ASN1ObjectIdentifier(isakmpOakley + ".1");
-    static final ASN1ObjectIdentifier    hmacSHA1     = new ASN1ObjectIdentifier(isakmpOakley + ".2");
+    /** IANA security mechanisms; 1.3.6.1.5.5 */
+    static final ASN1ObjectIdentifier    security_mechanisms  = security.branch("5");
+    /** IANA security nametypes;  1.3.6.1.5.6 */
+    static final ASN1ObjectIdentifier    security_nametypes   = security.branch("6");
+
+    /** PKIX base OID:            1.3.6.1.5.6.6 */
+    static final ASN1ObjectIdentifier    pkix                 = security_mechanisms.branch("6");
+
+
+    /** IPSEC base OID:                        1.3.6.1.5.5.8 */
+    static final ASN1ObjectIdentifier    ipsec                = security_mechanisms.branch("8");
+    /** IPSEC ISAKMP-Oakley OID:               1.3.6.1.5.5.8.1 */
+    static final ASN1ObjectIdentifier    isakmpOakley         = ipsec.branch("1");
+
+    /** IPSEC ISAKMP-Oakley hmacMD5 OID:       1.3.6.1.5.5.8.1.1 */
+    static final ASN1ObjectIdentifier    hmacMD5              = isakmpOakley.branch("1");
+    /** IPSEC ISAKMP-Oakley hmacSHA1 OID:      1.3.6.1.5.5.8.1.2 */
+    static final ASN1ObjectIdentifier    hmacSHA1             = isakmpOakley.branch("2");
     
-    static final ASN1ObjectIdentifier    hmacTIGER     = new ASN1ObjectIdentifier(isakmpOakley + ".3");
+    /** IPSEC ISAKMP-Oakley hmacTIGER OID:     1.3.6.1.5.5.8.1.3 */
+    static final ASN1ObjectIdentifier    hmacTIGER            = isakmpOakley.branch("3");
     
-    static final ASN1ObjectIdentifier    hmacRIPEMD160 = new ASN1ObjectIdentifier(isakmpOakley + ".4");
+    /** IPSEC ISAKMP-Oakley hmacRIPEMD160 OID: 1.3.6.1.5.5.8.1.4 */
+    static final ASN1ObjectIdentifier    hmacRIPEMD160        = isakmpOakley.branch("4");
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
index 0b5da2b..3c271da 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/icao/ICAOObjectIdentifiers.java
@@ -2,32 +2,48 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ *
+ * { ISOITU(2) intorgs(23) icao(136) }
+ */
 public interface ICAOObjectIdentifiers
 {
     //
     // base id
     //
+    /**  2.23.136  */
     static final ASN1ObjectIdentifier    id_icao                   = new ASN1ObjectIdentifier("2.23.136");
 
+    /**  2.23.136.1  */
     static final ASN1ObjectIdentifier    id_icao_mrtd              = id_icao.branch("1");
+    /**  2.23.136.1.1  */
     static final ASN1ObjectIdentifier    id_icao_mrtd_security     = id_icao_mrtd.branch("1");
 
-    // LDS security object, see ICAO Doc 9303-Volume 2-Section IV-A3.2
+    /** LDS security object, see ICAO Doc 9303-Volume 2-Section IV-A3.2<p>
+     *  2.23.136.1.1.1  */
     static final ASN1ObjectIdentifier    id_icao_ldsSecurityObject = id_icao_mrtd_security.branch("1");
 
-    // CSCA master list, see TR CSCA Countersigning and Master List issuance
+    /** CSCA master list, see TR CSCA Countersigning and Master List issuance<p>
+     * 2.23.136.1.1.2
+     */
     static final ASN1ObjectIdentifier    id_icao_cscaMasterList    = id_icao_mrtd_security.branch("2");
+    /** 2.23.136.1.1.3 */
     static final ASN1ObjectIdentifier    id_icao_cscaMasterListSigningKey = id_icao_mrtd_security.branch("3");
 
-    // document type list, see draft TR LDS and PKI Maintenance, par. 3.2.1
+    /** document type list, see draft TR LDS and PKI Maintenance, par. 3.2.1 <p>
+     * 2.23.136.1.1.4
+     */
     static final ASN1ObjectIdentifier    id_icao_documentTypeList  = id_icao_mrtd_security.branch("4");
 
-    // Active Authentication protocol, see draft TR LDS and PKI Maintenance,
-    // par. 5.2.2
+    /** Active Authentication protocol, see draft TR LDS and PKI Maintenance, par. 5.2.2<p>
+     * 2.23.136.1.1.5
+     */
     static final ASN1ObjectIdentifier    id_icao_aaProtocolObject  = id_icao_mrtd_security.branch("5");
 
-    // CSCA name change and key reoll-over, see draft TR LDS and PKI
-    // Maintenance, par. 3.2.1
+    /** CSCA name change and key reoll-over, see draft TR LDS and PKI Maintenance, par. 3.2.1<p>
+     * 2.23.136.1.1.6
+     */
     static final ASN1ObjectIdentifier    id_icao_extensions        = id_icao_mrtd_security.branch("6");
+    /** 2.23.136.1.1.6.1 */
     static final ASN1ObjectIdentifier    id_icao_extensions_namechangekeyrollover = id_icao_extensions.branch("1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/icao/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/icao/package.html
deleted file mode 100644
index f2301db..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/icao/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-ICAO ASN.1 classes for electronic passport.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
index bc2ac8d..6b75fde 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/isismtt/ISISMTTObjectIdentifiers.java
@@ -2,11 +2,16 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * ISISMT -- Industrial Signature Interoperability Specification
+ */
 public interface ISISMTTObjectIdentifiers
 {
 
+    /** 1.3.36.8 */
     static final ASN1ObjectIdentifier id_isismtt = new ASN1ObjectIdentifier("1.3.36.8");
 
+    /** 1.3.36.8.1 */
     static final ASN1ObjectIdentifier id_isismtt_cp = id_isismtt.branch("1");
 
     /**
@@ -15,29 +20,37 @@
      * Parliament and of the Council of 13 December 1999 on a Community
      * Framework for Electronic Signatures, which additionally conforms the
      * special requirements of the SigG and has been issued by an accredited CA.
+     * <p>
+     * 1.3.36.8.1.1
      */
+
     static final ASN1ObjectIdentifier id_isismtt_cp_accredited = id_isismtt_cp.branch("1");
 
+    /** 1.3.36.8.3 */
     static final ASN1ObjectIdentifier id_isismtt_at = id_isismtt.branch("3");
 
     /**
      * Certificate extensionDate of certificate generation
-     * 
      * <pre>
-     *                DateOfCertGenSyntax ::= GeneralizedTime
+     *     DateOfCertGenSyntax ::= GeneralizedTime
      * </pre>
+     * OID: 1.3.36.8.3.1
      */
     static final ASN1ObjectIdentifier id_isismtt_at_dateOfCertGen = id_isismtt_at.branch("1");
 
     /**
      * Attribute to indicate that the certificate holder may sign in the name of
      * a third person. May also be used as extension in a certificate.
+     * <p>
+     * OID: 1.3.36.8.3.2
      */
     static final ASN1ObjectIdentifier id_isismtt_at_procuration = id_isismtt_at.branch("2");
 
     /**
      * Attribute to indicate admissions to certain professions. May be used as
      * attribute in attribute certificate or as extension in a certificate
+     * <p>
+     * OID: 1.3.36.8.3.3
      */
     static final ASN1ObjectIdentifier id_isismtt_at_admission = id_isismtt_at.branch("3");
 
@@ -47,33 +60,37 @@
      * MonetaryLimit since January 1, 2004. For the sake of backward
      * compatibility with certificates already in use, SigG conforming
      * components MUST support MonetaryLimit (as well as QcEuLimitValue).
+     * <p>
+     * OID: 1.3.36.8.3.4
      */
     static final ASN1ObjectIdentifier id_isismtt_at_monetaryLimit = id_isismtt_at.branch("4");
 
     /**
      * A declaration of majority. May be used as attribute in attribute
      * certificate or as extension in a certificate
+     * <p>
+     * OID: 1.3.36.8.3.5
      */
     static final ASN1ObjectIdentifier id_isismtt_at_declarationOfMajority = id_isismtt_at.branch("5");
 
     /**
-     * 
      * Serial number of the smart card containing the corresponding private key
-     * 
      * <pre>
-     *                 ICCSNSyntax ::= OCTET STRING (SIZE(8..20))
+     *    ICCSNSyntax ::= OCTET STRING (SIZE(8..20))
      * </pre>
+     * <p>
+     * OID: 1.3.36.8.3.6
      */
     static final ASN1ObjectIdentifier id_isismtt_at_iCCSN = id_isismtt_at.branch("6");
 
     /**
-     * 
      * Reference for a file of a smartcard that stores the public key of this
-     * certificate and that is used as �security anchor�.
-     * 
+     * certificate and that is used as "security anchor".
      * <pre>
-     *      PKReferenceSyntax ::= OCTET STRING (SIZE(20))
+     *    PKReferenceSyntax ::= OCTET STRING (SIZE(20))
      * </pre>
+     * <p>
+     * OID: 1.3.36.8.3.7
      */
     static final ASN1ObjectIdentifier id_isismtt_at_PKReference = id_isismtt_at.branch("7");
 
@@ -81,28 +98,28 @@
      * Some other restriction regarding the usage of this certificate. May be
      * used as attribute in attribute certificate or as extension in a
      * certificate.
-     * 
      * <pre>
-     *             RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
+     *    RestrictionSyntax ::= DirectoryString (SIZE(1..1024))
      * </pre>
+     * <p>
+     * OID: 1.3.36.8.3.8
      * 
      * @see org.bouncycastle.asn1.isismtt.x509.Restriction
      */
     static final ASN1ObjectIdentifier id_isismtt_at_restriction = id_isismtt_at.branch("8");
 
     /**
-     * 
      * (Single)Request extension: Clients may include this extension in a
      * (single) Request to request the responder to send the certificate in the
      * response message along with the status information. Besides the LDAP
      * service, this extension provides another mechanism for the distribution
      * of certificates, which MAY optionally be provided by certificate
      * repositories.
-     * 
      * <pre>
-     *        RetrieveIfAllowed ::= BOOLEAN
-     *       
+     *    RetrieveIfAllowed ::= BOOLEAN
      * </pre>
+     * <p>
+     * OID: 1.3.36.8.3.9
      */
     static final ASN1ObjectIdentifier id_isismtt_at_retrieveIfAllowed = id_isismtt_at.branch("9");
 
@@ -110,6 +127,8 @@
      * SingleOCSPResponse extension: The certificate requested by the client by
      * inserting the RetrieveIfAllowed extension in the request, will be
      * returned in this extension.
+     * <p>
+     * OID: 1.3.36.8.3.10
      * 
      * @see org.bouncycastle.asn1.isismtt.ocsp.RequestedCertificate
      */
@@ -117,6 +136,8 @@
 
     /**
      * Base ObjectIdentifier for naming authorities
+     * <p>
+     * OID: 1.3.36.8.3.11
      */
     static final ASN1ObjectIdentifier id_isismtt_at_namingAuthorities = id_isismtt_at.branch("11");
 
@@ -127,13 +148,17 @@
      * this extension in the responses.
      * 
      * <pre>
-     *      CertInDirSince ::= GeneralizedTime
+     *    CertInDirSince ::= GeneralizedTime
      * </pre>
+     * <p>
+     * OID: 1.3.36.8.3.12
      */
     static final ASN1ObjectIdentifier id_isismtt_at_certInDirSince = id_isismtt_at.branch("12");
 
     /**
      * Hash of a certificate in OCSP.
+     * <p>
+     * OID: 1.3.36.8.3.13
      * 
      * @see org.bouncycastle.asn1.isismtt.ocsp.CertHash
      */
@@ -141,11 +166,13 @@
 
     /**
      * <pre>
-     *          NameAtBirth ::= DirectoryString(SIZE(1..64)
+     *    NameAtBirth ::= DirectoryString(SIZE(1..64)
      * </pre>
      * 
      * Used in
      * {@link org.bouncycastle.asn1.x509.SubjectDirectoryAttributes SubjectDirectoryAttributes}
+     * <p>
+     * OID: 1.3.36.8.3.14
      */
     static final ASN1ObjectIdentifier id_isismtt_at_nameAtBirth = id_isismtt_at.branch("14");
 
@@ -155,8 +182,10 @@
      * extension in a certificate.
      * 
      * <pre>
-     *               AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048))
+     *    AdditionalInformationSyntax ::= DirectoryString (SIZE(1..2048))
      * </pre>
+     * <p>
+     * OID: 1.3.36.8.3.15
      * 
      * @see org.bouncycastle.asn1.isismtt.x509.AdditionalInformationSyntax
      */
@@ -171,10 +200,11 @@
      * PKC as base certificate) contains some attribute that restricts the
      * usability of the PKC too. Attribute certificates with restricting content
      * MUST always be included in the signed document.
-     * 
      * <pre>
-     *                   LiabilityLimitationFlagSyntax ::= BOOLEAN
+     *    LiabilityLimitationFlagSyntax ::= BOOLEAN
      * </pre>
+     * <p>
+     * OID: 0.2.262.1.10.12.0
      */
     static final ASN1ObjectIdentifier id_isismtt_at_liabilityLimitationFlag = new ASN1ObjectIdentifier("0.2.262.1.10.12.0");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
index 73e0c58..73575f1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/kisa/KISAObjectIdentifiers.java
@@ -2,8 +2,30 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * Korea Information Security Agency (KISA)
+ * ({iso(1) member-body(2) kr(410) kisa(200004)})
+ * <p>
+ * See <a href="http://tools.ietf.org/html/rfc4010">RFC 4010</a>
+ * Use of the SEED Encryption Algorithm
+ * in Cryptographic Message Syntax (CMS),
+ * and <a href="http://tools.ietf.org/html/rfc4269">RFC 4269</a>
+ * The SEED Encryption Algorithm
+ */
 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");
+    /** RFC 4010, 4269: id-seedCBC; OID 1.2.410.200004.1.4 */
+    static final ASN1ObjectIdentifier id_seedCBC = new ASN1ObjectIdentifier("1.2.410.200004.1.4");
+
+    /** RFC 4269: id-seedMAC; OID 1.2.410.200004.1.7 */
+    static final ASN1ObjectIdentifier id_seedMAC = new ASN1ObjectIdentifier("1.2.410.200004.1.7");
+
+    /** RFC 4269: pbeWithSHA1AndSEED-CBC; OID 1.2.410.200004.1.15 */
+    static final ASN1ObjectIdentifier pbeWithSHA1AndSEED_CBC = new ASN1ObjectIdentifier("1.2.410.200004.1.15");
+
+    /** RFC 4010: id-npki-app-cmsSeed-wrap; OID 1.2.410.200004.7.1.1.1 */
+    static final ASN1ObjectIdentifier id_npki_app_cmsSeed_wrap = new ASN1ObjectIdentifier("1.2.410.200004.7.1.1.1");
+
+    /** RFC 4010: SeedEncryptionAlgorithmInCMS; OID 1.2.840.113549.1.9.16.0.24 */
+    static final ASN1ObjectIdentifier id_mod_cms_seed = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.0.24");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java
index f40a943..8ba2cf5 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/microsoft/MicrosoftObjectIdentifiers.java
@@ -2,16 +2,27 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * Microsoft
+ * <p>
+ * Object identifier base:
+ * <pre>
+ *    iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311)
+ * </pre>
+ * 1.3.6.1.4.1.311
+ */
 public interface MicrosoftObjectIdentifiers
 {
-    //
-    // Microsoft
-    //       iso(1) identified-organization(3) dod(6) internet(1) private(4) enterprise(1) microsoft(311)
-    //
+    /** Base OID: 1.3.6.1.4.1.311 */
     static final ASN1ObjectIdentifier    microsoft               = new ASN1ObjectIdentifier("1.3.6.1.4.1.311");
+    /** OID: 1.3.6.1.4.1.311.20.2 */
     static final ASN1ObjectIdentifier    microsoftCertTemplateV1 = microsoft.branch("20.2");
+    /** OID: 1.3.6.1.4.1.311.21.1 */
     static final ASN1ObjectIdentifier    microsoftCaVersion      = microsoft.branch("21.1");
+    /** OID: 1.3.6.1.4.1.311.21.2 */
     static final ASN1ObjectIdentifier    microsoftPrevCaCertHash = microsoft.branch("21.2");
+    /** OID: 1.3.6.1.4.1.311.21.7 */
     static final ASN1ObjectIdentifier    microsoftCertTemplateV2 = microsoft.branch("21.7");
+    /** OID: 1.3.6.1.4.1.311.21.10 */
     static final ASN1ObjectIdentifier    microsoftAppPolicies    = microsoft.branch("21.10");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
index debf268..6aff988 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/misc/MiscObjectIdentifiers.java
@@ -8,40 +8,52 @@
     // Netscape
     //       iso/itu(2) joint-assign(16) us(840) uscompany(1) netscape(113730) cert-extensions(1) }
     //
+    /** Netscape cert extensions OID base: 2.16.840.1.113730.1  */
     static final ASN1ObjectIdentifier    netscape                = new ASN1ObjectIdentifier("2.16.840.1.113730.1");
+    /** Netscape cert CertType OID: 2.16.840.1.113730.1.1  */
     static final ASN1ObjectIdentifier    netscapeCertType        = netscape.branch("1");
+    /** Netscape cert BaseURL OID: 2.16.840.1.113730.1.2  */
     static final ASN1ObjectIdentifier    netscapeBaseURL         = netscape.branch("2");
+    /** Netscape cert RevocationURL OID: 2.16.840.1.113730.1.3  */
     static final ASN1ObjectIdentifier    netscapeRevocationURL   = netscape.branch("3");
+    /** Netscape cert CARevocationURL OID: 2.16.840.1.113730.1.4  */
     static final ASN1ObjectIdentifier    netscapeCARevocationURL = netscape.branch("4");
+    /** Netscape cert RenewalURL OID: 2.16.840.1.113730.1.7  */
     static final ASN1ObjectIdentifier    netscapeRenewalURL      = netscape.branch("7");
+    /** Netscape cert CApolicyURL OID: 2.16.840.1.113730.1.8  */
     static final ASN1ObjectIdentifier    netscapeCApolicyURL     = netscape.branch("8");
+    /** Netscape cert SSLServerName OID: 2.16.840.1.113730.1.12  */
     static final ASN1ObjectIdentifier    netscapeSSLServerName   = netscape.branch("12");
+    /** Netscape cert CertComment OID: 2.16.840.1.113730.1.13  */
     static final ASN1ObjectIdentifier    netscapeCertComment     = netscape.branch("13");
     
     //
     // Verisign
     //       iso/itu(2) joint-assign(16) us(840) uscompany(1) verisign(113733) cert-extensions(1) }
     //
+    /** Verisign OID base: 2.16.840.1.113733.1 */
     static final ASN1ObjectIdentifier   verisign                = new ASN1ObjectIdentifier("2.16.840.1.113733.1");
 
-    //
-    // CZAG - country, zip, age, and gender
-    //
+    /** Verisign CZAG (Country,Zip,Age,Gender) Extension OID: 2.16.840.1.113733.1.6.3 */
     static final ASN1ObjectIdentifier    verisignCzagExtension   = verisign.branch("6.3");
-    // D&B D-U-N-S number
+    /** Verisign D&B D-U-N-S number Extension OID: 2.16.840.1.113733.1.6.15 */
     static final ASN1ObjectIdentifier    verisignDnbDunsNumber   = verisign.branch("6.15");
 
     //
     // Novell
     //       iso/itu(2) country(16) us(840) organization(1) novell(113719)
     //
+    /** Novell OID base: 2.16.840.1.113719 */
     static final ASN1ObjectIdentifier    novell                  = new ASN1ObjectIdentifier("2.16.840.1.113719");
+    /** Novell SecurityAttribs OID: 2.16.840.1.113719.1.9.4.1 */
     static final ASN1ObjectIdentifier    novellSecurityAttribs   = novell.branch("1.9.4.1");
 
     //
     // Entrust
     //       iso(1) member-body(16) us(840) nortelnetworks(113533) entrust(7)
     //
+    /** NortelNetworks Entrust OID base: 1.2.840.113533.7 */
     static final ASN1ObjectIdentifier    entrust                 = new ASN1ObjectIdentifier("1.2.840.113533.7");
+    /** NortelNetworks Entrust VersionExtension OID: 1.2.840.113533.7.65.0 */
     static final ASN1ObjectIdentifier    entrustVersionExtension = entrust.branch("65.0");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/misc/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/misc/package.html
deleted file mode 100644
index e3bda64..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/misc/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Miscellaneous object identifiers and objects.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/package.html
deleted file mode 100644
index 40776b0..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/mozilla/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding objects used by mozilla.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
index afa93c4..e3613c6 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/nist/NISTObjectIdentifiers.java
@@ -2,59 +2,95 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ *
+ * NIST:
+ *     iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3) 
+ */
 public interface NISTObjectIdentifiers
 {
     //
-    // NIST
-    //     iso/itu(2) joint-assign(16) us(840) organization(1) gov(101) csor(3) 
-
-    //
     // nistalgorithms(4)
     //
+    /** 2.16.840.1.101.3.4 -- algorithms */
     static final ASN1ObjectIdentifier    nistAlgorithm           = new ASN1ObjectIdentifier("2.16.840.1.101.3.4");
 
+    /** 2.16.840.1.101.3.4.2 */
     static final ASN1ObjectIdentifier    hashAlgs                = nistAlgorithm.branch("2");
 
+    /** 2.16.840.1.101.3.4.2.1 */
     static final ASN1ObjectIdentifier    id_sha256               = hashAlgs.branch("1");
+    /** 2.16.840.1.101.3.4.2.2 */
     static final ASN1ObjectIdentifier    id_sha384               = hashAlgs.branch("2");
+    /** 2.16.840.1.101.3.4.2.3 */
     static final ASN1ObjectIdentifier    id_sha512               = hashAlgs.branch("3");
+    /** 2.16.840.1.101.3.4.2.4 */
     static final ASN1ObjectIdentifier    id_sha224               = hashAlgs.branch("4");
+    /** 2.16.840.1.101.3.4.2.5 */
     static final ASN1ObjectIdentifier    id_sha512_224           = hashAlgs.branch("5");
+    /** 2.16.840.1.101.3.4.2.6 */
     static final ASN1ObjectIdentifier    id_sha512_256           = hashAlgs.branch("6");
 
-    static final ASN1ObjectIdentifier    aes                     =  nistAlgorithm.branch("1");
+    /** 2.16.840.1.101.3.4.1 */
+    static final ASN1ObjectIdentifier    aes                     = nistAlgorithm.branch("1");
     
+    /** 2.16.840.1.101.3.4.1.1 */
     static final ASN1ObjectIdentifier    id_aes128_ECB           = aes.branch("1"); 
+    /** 2.16.840.1.101.3.4.1.2 */
     static final ASN1ObjectIdentifier    id_aes128_CBC           = aes.branch("2");
+    /** 2.16.840.1.101.3.4.1.3 */
     static final ASN1ObjectIdentifier    id_aes128_OFB           = aes.branch("3"); 
+    /** 2.16.840.1.101.3.4.1.4 */
     static final ASN1ObjectIdentifier    id_aes128_CFB           = aes.branch("4"); 
+    /** 2.16.840.1.101.3.4.1.5 */
     static final ASN1ObjectIdentifier    id_aes128_wrap          = aes.branch("5");
+    /** 2.16.840.1.101.3.4.1.6 */
     static final ASN1ObjectIdentifier    id_aes128_GCM           = aes.branch("6");
+    /** 2.16.840.1.101.3.4.1.7 */
     static final ASN1ObjectIdentifier    id_aes128_CCM           = aes.branch("7");
     
+    /** 2.16.840.1.101.3.4.1.21 */
     static final ASN1ObjectIdentifier    id_aes192_ECB           = aes.branch("21"); 
+    /** 2.16.840.1.101.3.4.1.22 */
     static final ASN1ObjectIdentifier    id_aes192_CBC           = aes.branch("22"); 
+    /** 2.16.840.1.101.3.4.1.23 */
     static final ASN1ObjectIdentifier    id_aes192_OFB           = aes.branch("23"); 
+    /** 2.16.840.1.101.3.4.1.24 */
     static final ASN1ObjectIdentifier    id_aes192_CFB           = aes.branch("24"); 
+    /** 2.16.840.1.101.3.4.1.25 */
     static final ASN1ObjectIdentifier    id_aes192_wrap          = aes.branch("25");
+    /** 2.16.840.1.101.3.4.1.26 */
     static final ASN1ObjectIdentifier    id_aes192_GCM           = aes.branch("26");
+    /** 2.16.840.1.101.3.4.1.27 */
     static final ASN1ObjectIdentifier    id_aes192_CCM           = aes.branch("27");
     
+    /** 2.16.840.1.101.3.4.1.41 */
     static final ASN1ObjectIdentifier    id_aes256_ECB           = aes.branch("41"); 
+    /** 2.16.840.1.101.3.4.1.42 */
     static final ASN1ObjectIdentifier    id_aes256_CBC           = aes.branch("42");
+    /** 2.16.840.1.101.3.4.1.43 */
     static final ASN1ObjectIdentifier    id_aes256_OFB           = aes.branch("43"); 
+    /** 2.16.840.1.101.3.4.1.44 */
     static final ASN1ObjectIdentifier    id_aes256_CFB           = aes.branch("44"); 
+    /** 2.16.840.1.101.3.4.1.45 */
     static final ASN1ObjectIdentifier    id_aes256_wrap          = aes.branch("45"); 
+    /** 2.16.840.1.101.3.4.1.46 */
     static final ASN1ObjectIdentifier    id_aes256_GCM           = aes.branch("46");
+    /** 2.16.840.1.101.3.4.1.47 */
     static final ASN1ObjectIdentifier    id_aes256_CCM           = aes.branch("47");
 
     //
     // signatures
     //
+    /** 2.16.840.1.101.3.4.3 */
     static final ASN1ObjectIdentifier    id_dsa_with_sha2        = nistAlgorithm.branch("3");
 
+    /** 2.16.840.1.101.3.4.3.1 */
     static final ASN1ObjectIdentifier    dsa_with_sha224         = id_dsa_with_sha2.branch("1");
+    /** 2.16.840.1.101.3.4.3.2 */
     static final ASN1ObjectIdentifier    dsa_with_sha256         = id_dsa_with_sha2.branch("2");
+    /** 2.16.840.1.101.3.4.3.3 */
     static final ASN1ObjectIdentifier    dsa_with_sha384         = id_dsa_with_sha2.branch("3");
+    /** 2.16.840.1.101.3.4.3.4 */
     static final ASN1ObjectIdentifier    dsa_with_sha512         = id_dsa_with_sha2.branch("4");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/nist/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/nist/package.html
deleted file mode 100644
index 1cdca76..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/nist/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes for NIST related objects.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
index 2e4132a..fa32068 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ntt/NTTObjectIdentifiers.java
@@ -3,15 +3,23 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
 /**
- * From RFC 3657
+ * From <a href="http://tools.ietf.org/html/rfc3657">RFC 3657</a>
+ * Use of the Camellia Encryption Algorithm
+ * in Cryptographic Message Syntax (CMS)
  */
 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");
+    /** id-camellia128-cbc; OID 1.2.392.200011.61.1.1.1.2 */
+    static final ASN1ObjectIdentifier id_camellia128_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.2");
+    /** id-camellia192-cbc; OID 1.2.392.200011.61.1.1.1.3 */
+    static final ASN1ObjectIdentifier id_camellia192_cbc = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.1.3");
+    /** id-camellia256-cbc; OID 1.2.392.200011.61.1.1.1.4 */
+    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");
+    /** id-camellia128-wrap; OID 1.2.392.200011.61.1.1.3.2 */
+    static final ASN1ObjectIdentifier id_camellia128_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.2");
+    /** id-camellia192-wrap; OID 1.2.392.200011.61.1.1.3.3 */
+    static final ASN1ObjectIdentifier id_camellia192_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.3");
+    /** id-camellia256-wrap; OID 1.2.392.200011.61.1.1.3.4 */
+    static final ASN1ObjectIdentifier id_camellia256_wrap = new ASN1ObjectIdentifier("1.2.392.200011.61.1.1.3.4");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
index 40b15e9..f8ea8f7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/OCSPObjectIdentifiers.java
@@ -2,21 +2,28 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * OIDs for <a href="http://tools.ietf.org/html/rfc2560">RFC 2560</a>
+ * Online Certificate Status Protocol - OCSP.
+ */
 public interface OCSPObjectIdentifiers
 {
-    public static final String pkix_ocsp = "1.3.6.1.5.5.7.48.1";
-
-    public static final ASN1ObjectIdentifier id_pkix_ocsp = new ASN1ObjectIdentifier(pkix_ocsp);
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_basic = new ASN1ObjectIdentifier(pkix_ocsp + ".1");
+    /** OID: 1.3.6.1.5.5.7.48.1 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp       = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1");
+    /** OID: 1.3.6.1.5.5.7.48.1.1 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_basic = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.1");
     
-    //
-    // extensions
-    //
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_nonce = new ASN1ObjectIdentifier(pkix_ocsp + ".2");
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_crl = new ASN1ObjectIdentifier(pkix_ocsp + ".3");
+    /** OID: 1.3.6.1.5.5.7.48.1.2 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_nonce = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.2");
+    /** OID: 1.3.6.1.5.5.7.48.1.3 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_crl   = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.3");
     
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_response = new ASN1ObjectIdentifier(pkix_ocsp + ".4");
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_nocheck = new ASN1ObjectIdentifier(pkix_ocsp + ".5");
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_archive_cutoff = new ASN1ObjectIdentifier(pkix_ocsp + ".6");
-    public static final ASN1ObjectIdentifier id_pkix_ocsp_service_locator = new ASN1ObjectIdentifier(pkix_ocsp + ".7");
+    /** OID: 1.3.6.1.5.5.7.48.1.4 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_response        = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.4");
+    /** OID: 1.3.6.1.5.5.7.48.1.5 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_nocheck         = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.5");
+    /** OID: 1.3.6.1.5.5.7.48.1.6 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_archive_cutoff  = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.6");
+    /** OID: 1.3.6.1.5.5.7.48.1.7 */
+    static final ASN1ObjectIdentifier id_pkix_ocsp_service_locator = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.48.1.7");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
index 7279ae1..6770050 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/RevokedInfo.java
@@ -1,12 +1,12 @@
 package org.bouncycastle.asn1.ocsp;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Enumerated;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
-import org.bouncycastle.asn1.DEREnumerated;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.CRLReason;
@@ -32,8 +32,8 @@
 
         if (seq.size() > 1)
         {
-            this.revocationReason = CRLReason.getInstance(DEREnumerated.getInstance(
-                                (ASN1TaggedObject)seq.getObjectAt(1), true));
+            this.revocationReason = CRLReason.getInstance(ASN1Enumerated.getInstance(
+                (ASN1TaggedObject)seq.getObjectAt(1), true));
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/package.html
deleted file mode 100644
index 22c560d..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ocsp/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting OCSP objects.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
index c8ce26b..c169c16 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/OIWObjectIdentifiers.java
@@ -2,30 +2,49 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * OIW organization's OIDs:
+ * <p>
+ * id-SHA1 OBJECT IDENTIFIER ::=    
+ *   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }
+ */
 public interface OIWObjectIdentifiers
 {
-    // id-SHA1 OBJECT IDENTIFIER ::=    
-    //   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }    //
+    /** OID: 1.3.14.3.2.2 */
     static final ASN1ObjectIdentifier    md4WithRSA              = new ASN1ObjectIdentifier("1.3.14.3.2.2");
+    /** OID: 1.3.14.3.2.3 */
     static final ASN1ObjectIdentifier    md5WithRSA              = new ASN1ObjectIdentifier("1.3.14.3.2.3");
+    /** OID: 1.3.14.3.2.4 */
     static final ASN1ObjectIdentifier    md4WithRSAEncryption    = new ASN1ObjectIdentifier("1.3.14.3.2.4");
     
+    /** OID: 1.3.14.3.2.6 */
     static final ASN1ObjectIdentifier    desECB                  = new ASN1ObjectIdentifier("1.3.14.3.2.6");
+    /** OID: 1.3.14.3.2.7 */
     static final ASN1ObjectIdentifier    desCBC                  = new ASN1ObjectIdentifier("1.3.14.3.2.7");
+    /** OID: 1.3.14.3.2.8 */
     static final ASN1ObjectIdentifier    desOFB                  = new ASN1ObjectIdentifier("1.3.14.3.2.8");
+    /** OID: 1.3.14.3.2.9 */
     static final ASN1ObjectIdentifier    desCFB                  = new ASN1ObjectIdentifier("1.3.14.3.2.9");
 
+    /** OID: 1.3.14.3.2.17 */
     static final ASN1ObjectIdentifier    desEDE                  = new ASN1ObjectIdentifier("1.3.14.3.2.17");
     
+    /** OID: 1.3.14.3.2.26 */
     static final ASN1ObjectIdentifier    idSHA1                  = new ASN1ObjectIdentifier("1.3.14.3.2.26");
 
+    /** OID: 1.3.14.3.2.27 */
     static final ASN1ObjectIdentifier    dsaWithSHA1             = new ASN1ObjectIdentifier("1.3.14.3.2.27");
 
+    /** OID: 1.3.14.3.2.29 */
     static final ASN1ObjectIdentifier    sha1WithRSA             = new ASN1ObjectIdentifier("1.3.14.3.2.29");
     
-    // ElGamal Algorithm OBJECT IDENTIFIER ::=    
-    // {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 }
-    //
+    /**
+     * <pre>
+     * ElGamal Algorithm OBJECT IDENTIFIER ::=    
+     *   {iso(1) identified-organization(3) oiw(14) dirservsig(7) algorithm(2) encryption(1) 1 }
+     * </pre>
+     * OID: 1.3.14.7.2.1.1
+     */
     static final ASN1ObjectIdentifier    elGamalAlgorithm        = new ASN1ObjectIdentifier("1.3.14.7.2.1.1");
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/oiw/package.html
deleted file mode 100644
index 44eb2fe..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/oiw/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Objects and OID for the support of ISO OIW.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/package.html
deleted file mode 100644
index 1ac16a5..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-A library for parsing and writing ASN.1 objects. Support is provided for DER and BER encoding.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
index 65c0fa8..92c4e8f 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PBKDF2Params.java
@@ -9,16 +9,39 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * <pre>
+ *     PBKDF2-params ::= SEQUENCE {
+ *               salt CHOICE {
+ *                      specified OCTET STRING,
+ *                      otherSource AlgorithmIdentifier {{PBKDF2-SaltSources}}
+ *               },
+ *              iterationCount INTEGER (1..MAX),
+ *              keyLength INTEGER (1..MAX) OPTIONAL,
+ *              prf AlgorithmIdentifier {{PBKDF2-PRFs}} DEFAULT algid-hmacWithSHA1 }
+ * </pre>
+ */
 public class PBKDF2Params
     extends ASN1Object
 {
+    private static final AlgorithmIdentifier algid_hmacWithSHA1 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
+
     private ASN1OctetString octStr;
     private ASN1Integer      iterationCount;
     private ASN1Integer      keyLength;
+    private AlgorithmIdentifier prf;
 
+    /**
+     * Create PBKDF2Params from the passed in object,
+     *
+     * @param obj either PBKDF2Params or an ASN2Sequence.
+     * @return a PBKDF2Params instance.
+     */
     public static PBKDF2Params getInstance(
         Object  obj)
     {
@@ -34,7 +57,13 @@
 
         return null;
     }
-    
+
+    /**
+     * Create a PBKDF2Params with the specified salt, iteration count, and algid-hmacWithSHA1 for the prf.
+     *
+     * @param salt  input salt.
+     * @param iterationCount input iteration count.
+     */
     public PBKDF2Params(
         byte[]  salt,
         int     iterationCount)
@@ -43,6 +72,13 @@
         this.iterationCount = new ASN1Integer(iterationCount);
     }
 
+    /**
+     * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and algid-hmacWithSHA1 for the prf.
+     *
+     * @param salt  input salt.
+     * @param iterationCount input iteration count.
+     * @param keyLength intended key length to be produced.
+     */
     public PBKDF2Params(
         byte[]  salt,
         int     iterationCount,
@@ -53,6 +89,42 @@
         this.keyLength = new ASN1Integer(keyLength);
     }
 
+    /**
+     * Create a PBKDF2Params with the specified salt, iteration count, keyLength, and a defined prf.
+     *
+     * @param salt  input salt.
+     * @param iterationCount input iteration count.
+     * @param keyLength intended key length to be produced.
+     * @param prf the pseudo-random function to use.
+     */
+    public PBKDF2Params(
+        byte[]  salt,
+        int     iterationCount,
+        int     keyLength,
+        AlgorithmIdentifier prf)
+    {
+        this(salt, iterationCount);
+
+        this.keyLength = new ASN1Integer(keyLength);
+        this.prf = prf;
+    }
+
+    /**
+     * Create a PBKDF2Params with the specified salt, iteration count, and a defined prf.
+     *
+     * @param salt  input salt.
+     * @param iterationCount input iteration count.
+     * @param prf the pseudo-random function to use.
+     */
+    public PBKDF2Params(
+        byte[]  salt,
+        int     iterationCount,
+        AlgorithmIdentifier prf)
+    {
+        this(salt, iterationCount);
+        this.prf = prf;
+    }
+
     private PBKDF2Params(
         ASN1Sequence  seq)
     {
@@ -63,24 +135,57 @@
 
         if (e.hasMoreElements())
         {
-            keyLength = (ASN1Integer)e.nextElement();
-        }
-        else
-        {
-            keyLength = null;
+            Object o = e.nextElement();
+
+            if (o instanceof ASN1Integer)
+            {
+                keyLength = ASN1Integer.getInstance(o);
+                if (e.hasMoreElements())
+                {
+                    o = e.nextElement();
+                }
+                else
+                {
+                    o = null;
+                }
+            }
+            else
+            {
+                keyLength = null;
+            }
+
+            if (o != null)
+            {
+                prf = AlgorithmIdentifier.getInstance(o);
+            }
         }
     }
 
+    /**
+     * Return the salt to use.
+     *
+     * @return the input salt.
+     */
     public byte[] getSalt()
     {
         return octStr.getOctets();
     }
 
+    /**
+     * Return the iteration count to use.
+     *
+     * @return the input iteration count.
+     */
     public BigInteger getIterationCount()
     {
         return iterationCount.getValue();
     }
 
+    /**
+     * Return the intended length in octets of the derived key.
+     *
+     * @return length in octets for derived key, if specified.
+     */
     public BigInteger getKeyLength()
     {
         if (keyLength != null)
@@ -91,6 +196,36 @@
         return null;
     }
 
+    /**
+     * Return true if the PRF is the default (hmacWithSHA1)
+     *
+     * @return true if PRF is default, false otherwise.
+     */
+    public boolean isDefaultPrf()
+    {
+        return prf == null || prf.equals(algid_hmacWithSHA1);
+    }
+
+    /**
+     * Return the algId of the underlying pseudo random function to use.
+     *
+     * @return the prf algorithm identifier.
+     */
+    public AlgorithmIdentifier getPrf()
+    {
+        if (prf != null)
+        {
+            return prf;
+        }
+
+        return algid_hmacWithSHA1;
+    }
+
+    /**
+     * Return an ASN.1 structure suitable for encoding.
+     *
+     * @return the object as an ASN.1 encodable structure.
+     */
     public ASN1Primitive toASN1Primitive()
     {
         ASN1EncodableVector  v = new ASN1EncodableVector();
@@ -103,6 +238,11 @@
             v.add(keyLength);
         }
 
+        if (prf != null && !prf.equals(algid_hmacWithSHA1))
+        {
+            v.add(prf);
+        }
+
         return new DERSequence(v);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
index 405d0b4..05fced0 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/PKCSObjectIdentifiers.java
@@ -2,257 +2,389 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * pkcs-1 OBJECT IDENTIFIER ::=<p>
+ *   { iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
+ *
+ */
 public interface PKCSObjectIdentifiers
 {
-    //
-    // pkcs-1 OBJECT IDENTIFIER ::= {
-    //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 1 }
-    //
+    /** PKCS#1: 1.2.840.113549.1.1 */
     static final ASN1ObjectIdentifier    pkcs_1                    = new ASN1ObjectIdentifier("1.2.840.113549.1.1");
+    /** PKCS#1: 1.2.840.113549.1.1.1 */
     static final ASN1ObjectIdentifier    rsaEncryption             = pkcs_1.branch("1");
+    /** PKCS#1: 1.2.840.113549.1.1.2 */
     static final ASN1ObjectIdentifier    md2WithRSAEncryption      = pkcs_1.branch("2");
+    /** PKCS#1: 1.2.840.113549.1.1.3 */
     static final ASN1ObjectIdentifier    md4WithRSAEncryption      = pkcs_1.branch("3");
+    /** PKCS#1: 1.2.840.113549.1.1.4 */
     static final ASN1ObjectIdentifier    md5WithRSAEncryption      = pkcs_1.branch("4");
+    /** PKCS#1: 1.2.840.113549.1.1.5 */
     static final ASN1ObjectIdentifier    sha1WithRSAEncryption     = pkcs_1.branch("5");
+    /** PKCS#1: 1.2.840.113549.1.1.6 */
     static final ASN1ObjectIdentifier    srsaOAEPEncryptionSET     = pkcs_1.branch("6");
+    /** PKCS#1: 1.2.840.113549.1.1.7 */
     static final ASN1ObjectIdentifier    id_RSAES_OAEP             = pkcs_1.branch("7");
+    /** PKCS#1: 1.2.840.113549.1.1.8 */
     static final ASN1ObjectIdentifier    id_mgf1                   = pkcs_1.branch("8");
+    /** PKCS#1: 1.2.840.113549.1.1.9 */
     static final ASN1ObjectIdentifier    id_pSpecified             = pkcs_1.branch("9");
+    /** PKCS#1: 1.2.840.113549.1.1.10 */
     static final ASN1ObjectIdentifier    id_RSASSA_PSS             = pkcs_1.branch("10");
+    /** PKCS#1: 1.2.840.113549.1.1.11 */
     static final ASN1ObjectIdentifier    sha256WithRSAEncryption   = pkcs_1.branch("11");
+    /** PKCS#1: 1.2.840.113549.1.1.12 */
     static final ASN1ObjectIdentifier    sha384WithRSAEncryption   = pkcs_1.branch("12");
+    /** PKCS#1: 1.2.840.113549.1.1.13 */
     static final ASN1ObjectIdentifier    sha512WithRSAEncryption   = pkcs_1.branch("13");
+    /** PKCS#1: 1.2.840.113549.1.1.14 */
     static final ASN1ObjectIdentifier    sha224WithRSAEncryption   = pkcs_1.branch("14");
 
     //
     // pkcs-3 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 3 }
     //
+    /** PKCS#3: 1.2.840.113549.1.3 */
     static final ASN1ObjectIdentifier    pkcs_3                  = new ASN1ObjectIdentifier("1.2.840.113549.1.3");
+    /** PKCS#3: 1.2.840.113549.1.3.1 */
     static final ASN1ObjectIdentifier    dhKeyAgreement          = pkcs_3.branch("1");
 
     //
     // pkcs-5 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 5 }
     //
+    /** PKCS#5: 1.2.840.113549.1.5 */
     static final ASN1ObjectIdentifier    pkcs_5                  = new ASN1ObjectIdentifier("1.2.840.113549.1.5");
 
+    /** PKCS#5: 1.2.840.113549.1.5.1 */
     static final ASN1ObjectIdentifier    pbeWithMD2AndDES_CBC    = pkcs_5.branch("1");
+    /** PKCS#5: 1.2.840.113549.1.5.4 */
     static final ASN1ObjectIdentifier    pbeWithMD2AndRC2_CBC    = pkcs_5.branch("4");
+    /** PKCS#5: 1.2.840.113549.1.5.3 */
     static final ASN1ObjectIdentifier    pbeWithMD5AndDES_CBC    = pkcs_5.branch("3");
+    /** PKCS#5: 1.2.840.113549.1.5.6 */
     static final ASN1ObjectIdentifier    pbeWithMD5AndRC2_CBC    = pkcs_5.branch("6");
+    /** PKCS#5: 1.2.840.113549.1.5.10 */
     static final ASN1ObjectIdentifier    pbeWithSHA1AndDES_CBC   = pkcs_5.branch("10");
+    /** PKCS#5: 1.2.840.113549.1.5.11 */
     static final ASN1ObjectIdentifier    pbeWithSHA1AndRC2_CBC   = pkcs_5.branch("11");
-
+    /** PKCS#5: 1.2.840.113549.1.5.13 */
     static final ASN1ObjectIdentifier    id_PBES2                = pkcs_5.branch("13");
-
+    /** PKCS#5: 1.2.840.113549.1.5.12 */
     static final ASN1ObjectIdentifier    id_PBKDF2               = pkcs_5.branch("12");
 
     //
     // encryptionAlgorithm OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) 3 }
     //
+    /**  1.2.840.113549.3 */
     static final ASN1ObjectIdentifier    encryptionAlgorithm     = new ASN1ObjectIdentifier("1.2.840.113549.3");
 
+    /**  1.2.840.113549.3.7 */
     static final ASN1ObjectIdentifier    des_EDE3_CBC            = encryptionAlgorithm.branch("7");
+    /**  1.2.840.113549.3.2 */
     static final ASN1ObjectIdentifier    RC2_CBC                 = encryptionAlgorithm.branch("2");
+    /**  1.2.840.113549.3.4 */
     static final ASN1ObjectIdentifier    rc4                     = encryptionAlgorithm.branch("4");
 
     //
     // object identifiers for digests
     //
+    /**  1.2.840.113549.2 */
     static final ASN1ObjectIdentifier    digestAlgorithm        = new ASN1ObjectIdentifier("1.2.840.113549.2");
     //
     // md2 OBJECT IDENTIFIER ::=
     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 2}
     //
+    /**  1.2.840.113549.2.2 */
     static final ASN1ObjectIdentifier    md2                    = digestAlgorithm.branch("2");
 
     //
     // md4 OBJECT IDENTIFIER ::=
     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 4}
     //
-    static final ASN1ObjectIdentifier    md4 = digestAlgorithm.branch("4");
+    /**  1.2.840.113549.2.4 */
+    static final ASN1ObjectIdentifier    md4                    = digestAlgorithm.branch("4");
 
     //
     // md5 OBJECT IDENTIFIER ::=
     //      {iso(1) member-body(2) US(840) rsadsi(113549) digestAlgorithm(2) 5}
     //
-    static final ASN1ObjectIdentifier    md5                     = digestAlgorithm.branch("5");
+    /**  1.2.840.113549.2.5 */
+    static final ASN1ObjectIdentifier    md5                    = digestAlgorithm.branch("5");
 
-    static final ASN1ObjectIdentifier    id_hmacWithSHA1         = digestAlgorithm.branch("7");
-    static final ASN1ObjectIdentifier    id_hmacWithSHA224       = digestAlgorithm.branch("8");
-    static final ASN1ObjectIdentifier    id_hmacWithSHA256       = digestAlgorithm.branch("9");
-    static final ASN1ObjectIdentifier    id_hmacWithSHA384       = digestAlgorithm.branch("10");
-    static final ASN1ObjectIdentifier    id_hmacWithSHA512       = digestAlgorithm.branch("11");
+    /**  1.2.840.113549.2.7 */
+    static final ASN1ObjectIdentifier    id_hmacWithSHA1        = digestAlgorithm.branch("7");
+    /**  1.2.840.113549.2.8 */
+    static final ASN1ObjectIdentifier    id_hmacWithSHA224      = digestAlgorithm.branch("8");
+    /**  1.2.840.113549.2.9 */
+    static final ASN1ObjectIdentifier    id_hmacWithSHA256      = digestAlgorithm.branch("9");
+    /**  1.2.840.113549.2.10 */
+    static final ASN1ObjectIdentifier    id_hmacWithSHA384      = digestAlgorithm.branch("10");
+    /**  1.2.840.113549.2.11 */
+    static final ASN1ObjectIdentifier    id_hmacWithSHA512      = digestAlgorithm.branch("11");
 
     //
     // pkcs-7 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 7 }
     //
-    static final String                 pkcs_7                  = "1.2.840.113549.1.7";
-    static final ASN1ObjectIdentifier    data                    = new ASN1ObjectIdentifier(pkcs_7 + ".1");
-    static final ASN1ObjectIdentifier    signedData              = new ASN1ObjectIdentifier(pkcs_7 + ".2");
-    static final ASN1ObjectIdentifier    envelopedData           = new ASN1ObjectIdentifier(pkcs_7 + ".3");
-    static final ASN1ObjectIdentifier    signedAndEnvelopedData  = new ASN1ObjectIdentifier(pkcs_7 + ".4");
-    static final ASN1ObjectIdentifier    digestedData            = new ASN1ObjectIdentifier(pkcs_7 + ".5");
-    static final ASN1ObjectIdentifier    encryptedData           = new ASN1ObjectIdentifier(pkcs_7 + ".6");
+    /** pkcs#7: 1.2.840.113549.1.7 */
+    static final ASN1ObjectIdentifier    pkcs_7                  = new ASN1ObjectIdentifier("1.2.840.113549.1.7");
+    /** PKCS#7: 1.2.840.113549.1.7.1 */
+    static final ASN1ObjectIdentifier    data                    = new ASN1ObjectIdentifier("1.2.840.113549.1.7.1");
+    /** PKCS#7: 1.2.840.113549.1.7.2 */
+    static final ASN1ObjectIdentifier    signedData              = new ASN1ObjectIdentifier("1.2.840.113549.1.7.2");
+    /** PKCS#7: 1.2.840.113549.1.7.3 */
+    static final ASN1ObjectIdentifier    envelopedData           = new ASN1ObjectIdentifier("1.2.840.113549.1.7.3");
+    /** PKCS#7: 1.2.840.113549.1.7.4 */
+    static final ASN1ObjectIdentifier    signedAndEnvelopedData  = new ASN1ObjectIdentifier("1.2.840.113549.1.7.4");
+    /** PKCS#7: 1.2.840.113549.1.7.5 */
+    static final ASN1ObjectIdentifier    digestedData            = new ASN1ObjectIdentifier("1.2.840.113549.1.7.5");
+    /** PKCS#7: 1.2.840.113549.1.7.76 */
+    static final ASN1ObjectIdentifier    encryptedData           = new ASN1ObjectIdentifier("1.2.840.113549.1.7.6");
 
     //
     // pkcs-9 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 9 }
     //
+    /** PKCS#9: 1.2.840.113549.1.9 */
     static final ASN1ObjectIdentifier    pkcs_9                  = new ASN1ObjectIdentifier("1.2.840.113549.1.9");
 
-    static final ASN1ObjectIdentifier    pkcs_9_at_emailAddress  = pkcs_9.branch("1");
-    static final ASN1ObjectIdentifier    pkcs_9_at_unstructuredName = pkcs_9.branch("2");
-    static final ASN1ObjectIdentifier    pkcs_9_at_contentType = pkcs_9.branch("3");
-    static final ASN1ObjectIdentifier    pkcs_9_at_messageDigest = pkcs_9.branch("4");
-    static final ASN1ObjectIdentifier    pkcs_9_at_signingTime = pkcs_9.branch("5");
-    static final ASN1ObjectIdentifier    pkcs_9_at_counterSignature = pkcs_9.branch("6");
-    static final ASN1ObjectIdentifier    pkcs_9_at_challengePassword = pkcs_9.branch("7");
+    /** PKCS#9: 1.2.840.113549.1.9.1 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_emailAddress        = pkcs_9.branch("1");
+    /** PKCS#9: 1.2.840.113549.1.9.2 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_unstructuredName    = pkcs_9.branch("2");
+    /** PKCS#9: 1.2.840.113549.1.9.3 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_contentType         = pkcs_9.branch("3");
+    /** PKCS#9: 1.2.840.113549.1.9.4 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_messageDigest       = pkcs_9.branch("4");
+    /** PKCS#9: 1.2.840.113549.1.9.5 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_signingTime         = pkcs_9.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.6 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_counterSignature    = pkcs_9.branch("6");
+    /** PKCS#9: 1.2.840.113549.1.9.7 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_challengePassword   = pkcs_9.branch("7");
+    /** PKCS#9: 1.2.840.113549.1.9.8 */
     static final ASN1ObjectIdentifier    pkcs_9_at_unstructuredAddress = pkcs_9.branch("8");
+    /** PKCS#9: 1.2.840.113549.1.9.9 */
     static final ASN1ObjectIdentifier    pkcs_9_at_extendedCertificateAttributes = pkcs_9.branch("9");
 
+    /** PKCS#9: 1.2.840.113549.1.9.13 */
     static final ASN1ObjectIdentifier    pkcs_9_at_signingDescription = pkcs_9.branch("13");
-    static final ASN1ObjectIdentifier    pkcs_9_at_extensionRequest = pkcs_9.branch("14");
-    static final ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities = pkcs_9.branch("15");
+    /** PKCS#9: 1.2.840.113549.1.9.14 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_extensionRequest   = pkcs_9.branch("14");
+    /** PKCS#9: 1.2.840.113549.1.9.15 */
+    static final ASN1ObjectIdentifier    pkcs_9_at_smimeCapabilities  = pkcs_9.branch("15");
+    /** PKCS#9: 1.2.840.113549.1.9.16 */
+    static final ASN1ObjectIdentifier    id_smime                     = pkcs_9.branch("16");
 
+    /** PKCS#9: 1.2.840.113549.1.9.20 */
     static final ASN1ObjectIdentifier    pkcs_9_at_friendlyName  = pkcs_9.branch("20");
+    /** PKCS#9: 1.2.840.113549.1.9.21 */
     static final ASN1ObjectIdentifier    pkcs_9_at_localKeyId    = pkcs_9.branch("21");
 
-    /** @deprecated use x509Certificate instead */
+    /** PKCS#9: 1.2.840.113549.1.9.22.1
+     * @deprecated use x509Certificate instead */
     static final ASN1ObjectIdentifier    x509certType            = pkcs_9.branch("22.1");
 
+    /** PKCS#9: 1.2.840.113549.1.9.22 */
     static final ASN1ObjectIdentifier    certTypes               = pkcs_9.branch("22");
+    /** PKCS#9: 1.2.840.113549.1.9.22.1 */
     static final ASN1ObjectIdentifier    x509Certificate         = certTypes.branch("1");
+    /** PKCS#9: 1.2.840.113549.1.9.22.2 */
     static final ASN1ObjectIdentifier    sdsiCertificate         = certTypes.branch("2");
 
+    /** PKCS#9: 1.2.840.113549.1.9.23 */
     static final ASN1ObjectIdentifier    crlTypes                = pkcs_9.branch("23");
+    /** PKCS#9: 1.2.840.113549.1.9.23.1 */
     static final ASN1ObjectIdentifier    x509Crl                 = crlTypes.branch("1");
 
-    static final ASN1ObjectIdentifier    id_alg_PWRI_KEK    = pkcs_9.branch("16.3.9");
-
     //
     // SMIME capability sub oids.
     //
+    /** PKCS#9: 1.2.840.113549.1.9.15.1 -- smime capability */
     static final ASN1ObjectIdentifier    preferSignedData        = pkcs_9.branch("15.1");
+    /** PKCS#9: 1.2.840.113549.1.9.15.2 -- smime capability  */
     static final ASN1ObjectIdentifier    canNotDecryptAny        = pkcs_9.branch("15.2");
+    /** PKCS#9: 1.2.840.113549.1.9.15.3 -- smime capability  */
     static final ASN1ObjectIdentifier    sMIMECapabilitiesVersions = pkcs_9.branch("15.3");
 
     //
     // id-ct OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) ct(1)}
     //
+    /** PKCS#9: 1.2.840.113549.1.9.16.1 -- smime ct */
     static final ASN1ObjectIdentifier    id_ct = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.1");
 
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.2 -- smime ct authData */
     static final ASN1ObjectIdentifier    id_ct_authData          = id_ct.branch("2");
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.4 -- smime ct TSTInfo*/
     static final ASN1ObjectIdentifier    id_ct_TSTInfo           = id_ct.branch("4");
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.9 -- smime ct compressedData */
     static final ASN1ObjectIdentifier    id_ct_compressedData    = id_ct.branch("9");
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.23 -- smime ct authEnvelopedData */
     static final ASN1ObjectIdentifier    id_ct_authEnvelopedData = id_ct.branch("23");
+    /** PKCS#9: 1.2.840.113549.1.9.16.1.31 -- smime ct timestampedData*/
     static final ASN1ObjectIdentifier    id_ct_timestampedData   = id_ct.branch("31");
 
+
+    /** S/MIME: Algorithm Identifiers ; 1.2.840.113549.1.9.16.3 */
+    static final ASN1ObjectIdentifier id_alg                  = id_smime.branch("3");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.9 */
+    static final ASN1ObjectIdentifier id_alg_PWRI_KEK         = id_alg.branch("9");
+
     //
     // id-cti OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) cti(6)}
     //
+    /** PKCS#9: 1.2.840.113549.1.9.16.6 -- smime cti */
     static final ASN1ObjectIdentifier    id_cti = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.6");
     
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfOrigin  = id_cti.branch("1");
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfReceipt = id_cti.branch("2");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.1 -- smime cti proofOfOrigin */
+    static final ASN1ObjectIdentifier    id_cti_ets_proofOfOrigin   = id_cti.branch("1");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2 -- smime cti proofOfReceipt*/
+    static final ASN1ObjectIdentifier    id_cti_ets_proofOfReceipt  = id_cti.branch("2");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.3 -- smime cti proofOfDelivery */
     static final ASN1ObjectIdentifier    id_cti_ets_proofOfDelivery = id_cti.branch("3");
-    static final ASN1ObjectIdentifier    id_cti_ets_proofOfSender = id_cti.branch("4");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.4 -- smime cti proofOfSender */
+    static final ASN1ObjectIdentifier    id_cti_ets_proofOfSender   = id_cti.branch("4");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.5 -- smime cti proofOfApproval */
     static final ASN1ObjectIdentifier    id_cti_ets_proofOfApproval = id_cti.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.6 -- smime cti proofOfCreation */
     static final ASN1ObjectIdentifier    id_cti_ets_proofOfCreation = id_cti.branch("6");
     
     //
     // id-aa OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
     // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) attributes(2)}
     //
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2 - smime attributes */
     static final ASN1ObjectIdentifier    id_aa = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.2");
 
 
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.1 -- smime attribute receiptRequest */
     static final ASN1ObjectIdentifier id_aa_receiptRequest = id_aa.branch("1");
     
-    static final ASN1ObjectIdentifier id_aa_contentHint = id_aa.branch("4"); // See RFC 2634
-    static final ASN1ObjectIdentifier id_aa_msgSigDigest = id_aa.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.4 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
+    static final ASN1ObjectIdentifier id_aa_contentHint      = id_aa.branch("4"); // See RFC 2634
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.5 */
+    static final ASN1ObjectIdentifier id_aa_msgSigDigest     = id_aa.branch("5");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.10 */
     static final ASN1ObjectIdentifier id_aa_contentReference = id_aa.branch("10");
     /*
      * id-aa-encrypKeyPref OBJECT IDENTIFIER ::= {id-aa 11}
      * 
      */
-    static final ASN1ObjectIdentifier id_aa_encrypKeyPref = id_aa.branch("11");
-    static final ASN1ObjectIdentifier id_aa_signingCertificate = id_aa.branch("12");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.11 */
+    static final ASN1ObjectIdentifier id_aa_encrypKeyPref        = id_aa.branch("11");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.12 */
+    static final ASN1ObjectIdentifier id_aa_signingCertificate   = id_aa.branch("12");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.47 */
     static final ASN1ObjectIdentifier id_aa_signingCertificateV2 = id_aa.branch("47");
 
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.7 - See <a href="http://tools.ietf.org/html/rfc2634">RFC 2634</a> */
     static final ASN1ObjectIdentifier id_aa_contentIdentifier = id_aa.branch("7"); // See RFC 2634
 
     /*
      * RFC 3126
      */
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.14 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_signatureTimeStampToken = id_aa.branch("14");
     
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.15 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_sigPolicyId = id_aa.branch("15");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.16 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_commitmentType = id_aa.branch("16");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.17 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_signerLocation = id_aa.branch("17");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.18 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_signerAttr = id_aa.branch("18");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.19 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_otherSigCert = id_aa.branch("19");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.20 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_contentTimestamp = id_aa.branch("20");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.21 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_certificateRefs = id_aa.branch("21");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.22 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_revocationRefs = id_aa.branch("22");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.23 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_certValues = id_aa.branch("23");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.24 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_revocationValues = id_aa.branch("24");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.25 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_escTimeStamp = id_aa.branch("25");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.26 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_certCRLTimestamp = id_aa.branch("26");
+    /** PKCS#9: 1.2.840.113549.1.9.16.6.2.27 - <a href="http://tools.ietf.org/html/rfc3126">RFC 3126</a> */
     static final ASN1ObjectIdentifier id_aa_ets_archiveTimestamp = id_aa.branch("27");
 
     /** @deprecated use id_aa_ets_sigPolicyId instead */
-    static final ASN1ObjectIdentifier id_aa_sigPolicyId = id_aa_ets_sigPolicyId;
+    static final ASN1ObjectIdentifier id_aa_sigPolicyId    = id_aa_ets_sigPolicyId;
     /** @deprecated use id_aa_ets_commitmentType instead */
     static final ASN1ObjectIdentifier id_aa_commitmentType = id_aa_ets_commitmentType;
     /** @deprecated use id_aa_ets_signerLocation instead */
     static final ASN1ObjectIdentifier id_aa_signerLocation = id_aa_ets_signerLocation;
     /** @deprecated use id_aa_ets_otherSigCert instead */
-    static final ASN1ObjectIdentifier id_aa_otherSigCert = id_aa_ets_otherSigCert;
+    static final ASN1ObjectIdentifier id_aa_otherSigCert   = id_aa_ets_otherSigCert;
     
-    //
-    // id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
-    // rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)}
-    //
+    /**
+     * id-spq OBJECT IDENTIFIER ::= {iso(1) member-body(2) usa(840)
+     * rsadsi(113549) pkcs(1) pkcs-9(9) smime(16) id-spq(5)}; <p>
+     * 1.2.840.113549.1.9.16.5
+     */
     final String id_spq = "1.2.840.113549.1.9.16.5";
 
-    static final ASN1ObjectIdentifier id_spq_ets_uri = new ASN1ObjectIdentifier(id_spq + ".1");
+    /** SMIME SPQ URI:     1.2.840.113549.1.9.16.5.1 */
+    static final ASN1ObjectIdentifier id_spq_ets_uri     = new ASN1ObjectIdentifier(id_spq + ".1");
+    /** SMIME SPQ UNOTICE: 1.2.840.113549.1.9.16.5.2 */
     static final ASN1ObjectIdentifier id_spq_ets_unotice = new ASN1ObjectIdentifier(id_spq + ".2");
 
     //
     // pkcs-12 OBJECT IDENTIFIER ::= {
     //       iso(1) member-body(2) us(840) rsadsi(113549) pkcs(1) 12 }
     //
+    /** PKCS#12: 1.2.840.113549.1.12 */
     static final ASN1ObjectIdentifier   pkcs_12                  = new ASN1ObjectIdentifier("1.2.840.113549.1.12");
+    /** PKCS#12: 1.2.840.113549.1.12.10.1 */
     static final ASN1ObjectIdentifier   bagtypes                 = pkcs_12.branch("10.1");
 
+    /** PKCS#12: 1.2.840.113549.1.12.10.1.1 */
     static final ASN1ObjectIdentifier    keyBag                  = bagtypes.branch("1");
+    /** PKCS#12: 1.2.840.113549.1.12.10.1.2 */
     static final ASN1ObjectIdentifier    pkcs8ShroudedKeyBag     = bagtypes.branch("2");
+    /** PKCS#12: 1.2.840.113549.1.12.10.1.3 */
     static final ASN1ObjectIdentifier    certBag                 = bagtypes.branch("3");
+    /** PKCS#12: 1.2.840.113549.1.12.10.1.4 */
     static final ASN1ObjectIdentifier    crlBag                  = bagtypes.branch("4");
+    /** PKCS#12: 1.2.840.113549.1.12.10.1.5 */
     static final ASN1ObjectIdentifier    secretBag               = bagtypes.branch("5");
+    /** PKCS#12: 1.2.840.113549.1.12.10.1.6 */
     static final ASN1ObjectIdentifier    safeContentsBag         = bagtypes.branch("6");
 
-    static final ASN1ObjectIdentifier    pkcs_12PbeIds  = pkcs_12.branch("1");
+    /** PKCS#12: 1.2.840.113549.1.12.1 */
+    static final ASN1ObjectIdentifier    pkcs_12PbeIds           = pkcs_12.branch("1");
 
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC4 = pkcs_12PbeIds.branch("1");
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC4  = pkcs_12PbeIds.branch("2");
+    /** PKCS#12: 1.2.840.113549.1.12.1.1 */
+    static final ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC4          = pkcs_12PbeIds.branch("1");
+    /** PKCS#12: 1.2.840.113549.1.12.1.2 */
+    static final ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC4           = pkcs_12PbeIds.branch("2");
+    /** PKCS#12: 1.2.840.113549.1.12.1.3 */
     static final ASN1ObjectIdentifier    pbeWithSHAAnd3_KeyTripleDES_CBC = pkcs_12PbeIds.branch("3");
+    /** PKCS#12: 1.2.840.113549.1.12.1.4 */
     static final ASN1ObjectIdentifier    pbeWithSHAAnd2_KeyTripleDES_CBC = pkcs_12PbeIds.branch("4");
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC2_CBC = pkcs_12PbeIds.branch("5");
-    static final ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
+    /** PKCS#12: 1.2.840.113549.1.12.1.5 */
+    static final ASN1ObjectIdentifier    pbeWithSHAAnd128BitRC2_CBC      = pkcs_12PbeIds.branch("5");
+    /** PKCS#12: 1.2.840.113549.1.12.1.6 */
+    static final ASN1ObjectIdentifier    pbeWithSHAAnd40BitRC2_CBC       = pkcs_12PbeIds.branch("6");
 
     /**
+     * PKCS#12: 1.2.840.113549.1.12.1.6
      * @deprecated use pbeWithSHAAnd40BitRC2_CBC
      */
     static final ASN1ObjectIdentifier    pbewithSHAAnd40BitRC2_CBC = pkcs_12PbeIds.branch("6");
 
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.6 */
     static final ASN1ObjectIdentifier    id_alg_CMS3DESwrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.6");
-    static final ASN1ObjectIdentifier    id_alg_CMSRC2wrap = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
+    /** PKCS#9: 1.2.840.113549.1.9.16.3.7 */
+    static final ASN1ObjectIdentifier    id_alg_CMSRC2wrap  = new ASN1ObjectIdentifier("1.2.840.113549.1.9.16.3.7");
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
index 515b515..e707fd1 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/RSAESOAEPparams.java
@@ -57,7 +57,11 @@
         this.maskGenAlgorithm = maskGenAlgorithm;
         this.pSourceAlgorithm = pSourceAlgorithm;
     }
-    
+
+    /**
+     * @deprecated use getInstance()
+     * @param seq
+     */
     public RSAESOAEPparams(
         ASN1Sequence seq)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/package.html
deleted file mode 100644
index ab800f4..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/pkcs/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting the various RSA PKCS documents.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
index 4bf6b2b..df2238a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/ECPrivateKey.java
@@ -62,7 +62,7 @@
 
     public ECPrivateKey(
         BigInteger key,
-        ASN1Object parameters)
+        ASN1Encodable parameters)
     {
         this(key, null, parameters);
     }
@@ -70,7 +70,7 @@
     public ECPrivateKey(
         BigInteger key,
         DERBitString publicKey,
-        ASN1Object parameters)
+        ASN1Encodable parameters)
     {
         byte[] bytes = BigIntegers.asUnsignedByteArray(key);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
index 44c811b..50a7a63 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECNamedCurves.java
@@ -15,6 +15,21 @@
 
 public class SECNamedCurves
 {
+    private static ECCurve configureCurve(ECCurve curve)
+    {
+//        int coord = ECCurve.COORD_JACOBIAN_MODIFIED;
+//
+//        if (curve.getCoordinateSystem() != coord && curve.supportsCoordinateSystem(coord))
+//        {
+//            return curve.configure()
+//                .setCoordinateSystem(coord)
+////                .setMultiplier(new WNafL2RMultiplier())
+//                .create();
+//        }
+
+        return curve;
+    }
+
     private static BigInteger fromHex(
         String hex)
     {
@@ -36,7 +51,7 @@
             BigInteger n = fromHex("DB7C2ABF62E35E7628DFAC6561C5");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "09487239995A5EE76B55F9C2F098"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -62,7 +77,7 @@
             BigInteger n = fromHex("36DF0AAFD8B8D7597CA10520D04B");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "4BA30AB5E892B4E1649DD0928643"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -88,7 +103,7 @@
             BigInteger n = fromHex("FFFFFFFE0000000075A30D1B9038A115");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "161FF7528B899B2D0C28607CA52C5B86"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -114,7 +129,7 @@
             BigInteger n = fromHex("3FFFFFFF7FFFFFFFBE0024720613B5A3");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "7B6AA5D85E572983E6FB32A7CDEBC140"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -140,7 +155,7 @@
             BigInteger n = fromHex("0100000000000000000001B8FA16DFAB9ACA16B6B3");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
 //            ECPoint G = curve.decodePoint(Hex.decode("02"
 //                + "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -166,7 +181,7 @@
             BigInteger n = fromHex("0100000000000000000001F4C8F927AED3CA752257");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
                 //+ "4A96B5688EF573284664698968C38BB913CBFC82"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -192,7 +207,7 @@
             BigInteger n = fromHex("0100000000000000000000351EE786A818F3A1A16B");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "52DCB034293A117E1F4FF11B30F7199D3144CE6D"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -218,7 +233,7 @@
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -244,7 +259,7 @@
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -270,7 +285,7 @@
             BigInteger n = fromHex("010000000000000000000000000001DCE8D2EC6184CAF0A971769FB1F7");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "A1455B334DF099DF30FC28A169A467E9E47075A90F7E650EB6B7A45C"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -296,7 +311,7 @@
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -322,7 +337,7 @@
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -348,7 +363,7 @@
             BigInteger n = fromHex("FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -374,7 +389,7 @@
             BigInteger n = fromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -400,7 +415,8 @@
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409");
             BigInteger h = BigInteger.valueOf(1);
 
-            ECCurve curve = new ECCurve.Fp(p, a, b);
+            ECCurve curve = configureCurve(new ECCurve.Fp(p, a, b));
+
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -410,7 +426,7 @@
             return new X9ECParameters(curve, G, n, h, S);
         }
     };
-    
+
     /*
      * sect113r1
      */
@@ -427,7 +443,7 @@
             BigInteger n = fromHex("0100000000000000D9CCEC8A39E56F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "009D73616F35F4AB1407D73562C10F"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -454,7 +470,7 @@
             BigInteger n = fromHex("010000000000000108789B2496AF93");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "01A57A6A7B26CA5EF52FCDB8164797"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -483,7 +499,7 @@
             BigInteger n = fromHex("0400000000000000023123953A9464B54D");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "0081BAF91FDF9833C40F9C181343638399"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -512,7 +528,7 @@
             BigInteger n = fromHex("0400000000000000016954A233049BA98F");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "0356DCD8F2F95031AD652D23951BB366A8"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -541,7 +557,7 @@
             BigInteger n = fromHex("04000000000000000000020108A2E0CC0D99F8A5EF");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "02FE13C0537BBC11ACAA07D793DE4E6D5E5C94EEE8"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -570,7 +586,7 @@
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFF48AAB689C29CA710279B");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "0369979697AB43897789566789567F787A7876A654"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -599,7 +615,7 @@
             BigInteger n = fromHex("040000000000000000000292FE77E70C12A4234C33");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "03F0EBA16286A2D57EA0991168D4994637E8343E36"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -626,7 +642,7 @@
             BigInteger n = fromHex("01000000000000000000000000C7F34A778F443ACC920EBA49");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "01F481BC5F0FF84A74AD6CDF6FDEF4BF6179625372D8C0C5E1"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -653,7 +669,7 @@
             BigInteger n = fromHex("010000000000000000000000015AAB561B005413CCD4EE99D5");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "00D9B67D192E0367C803F39E1A7E82CA14A651350AAE617E8F"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -680,7 +696,7 @@
             BigInteger n = fromHex("8000000000000000000000000000069D5BB915BCD46EFB1AD5F173ABDF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "017232BA853A7E731AF129F22FF4149563A419C26BF50A4C9D6EEFAD6126"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -707,7 +723,7 @@
             BigInteger n = fromHex("01000000000000000000000000000013E974E72F8A6922031D2603CFE0D7");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "00FAC9DFCBAC8313BB2139F1BB755FEF65BC391F8B36F8F8EB7371FD558B"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -734,7 +750,7 @@
             BigInteger n = fromHex("2000000000000000000000000000005A79FEC67CB6E91F1C1DA800E478A5");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "29A0B6A887A983E9730988A68727A8B2D126C44CC2CC7B2A6555193035DC"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -763,7 +779,7 @@
             BigInteger n = fromHex("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE9AE2ED07577265DFF7F94451E061E163C61");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "0503213F78CA44883F1A3B8162F188E553CD265F23C1567A16876913B0C2AC2458492836"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -792,7 +808,7 @@
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEF90399660FC938A90165B042A7CEFADB307");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "05F939258DB7DD90E1934F8C70B0DFEC2EED25B8557EAC9C80E2E198F8CDBECD86B12053"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -819,7 +835,7 @@
             BigInteger n = fromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE5F83B2D4EA20400EC4557D5ED3E3E7CA5B4B5C83B8E01E5FCF");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "0060F05F658F49C1AD3AB1890F7184210EFD0987E307C84C27ACCFB8F9F67CC2C460189EB5AAAA62EE222EB1B35540CFE9023746"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -846,7 +862,7 @@
             BigInteger n = fromHex("010000000000000000000000000000000000000000000000000001E2AAD6A612F33307BE5FA47C3C9E052F838164CD37D9A21173");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "015D4860D088DDB3496B0C6064756260441CDE4AF1771D4DB01FFE5B34E59703DC255A868A1180515603AEAB60794E54BB7996A7"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -875,7 +891,7 @@
             BigInteger n = fromHex("020000000000000000000000000000000000000000000000000000000000000000000000131850E1F19A63E4B391A8DB917F4138B630D84BE5D639381E91DEB45CFE778F637C1001");
             BigInteger h = BigInteger.valueOf(4);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("02"
             //+ "026EB7A859923FBC82189631F8103FE4AC9CA2970012D5D46024804801841CA44370958493B205E647DA304DB4CEB08CBBD1BA39494776FB988B47174DCA88C7E2945283A01C8972"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
@@ -904,7 +920,7 @@
             BigInteger n = fromHex("03FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE661CE18FF55987308059B186823851EC7DD9CA1161DE93D5174D66E8382E9BB2FE84E47");
             BigInteger h = BigInteger.valueOf(2);
 
-            ECCurve curve = new ECCurve.F2m(m, k1, k2, k3, a, b, n, h);
+            ECCurve curve = configureCurve(new ECCurve.F2m(m, k1, k2, k3, a, b, n, h));
             //ECPoint G = curve.decodePoint(Hex.decode("03"
             //+ "0303001D34B856296C16C0D40D3CD7750A93D1D2955FA80AA5F40FC8DB7B2ABDBDE53950F4C0D293CDD711A35B67FB1499AE60038614F1394ABFA3B4C850D927E1E7769C8EEC2D19"));
             ECPoint G = curve.decodePoint(Hex.decode("04"
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
index 8b19cd6..fb60aca 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/sec/SECObjectIdentifiers.java
@@ -3,48 +3,85 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 
+/**
+ * Certicom object identifiers
+ * <pre>
+ *  ellipticCurve OBJECT IDENTIFIER ::= {
+ *        iso(1) identified-organization(3) certicom(132) curve(0)
+ *  }
+ * </pre>
+ */
 public interface SECObjectIdentifiers
 {
-    /**
-     *  ellipticCurve OBJECT IDENTIFIER ::= {
-     *        iso(1) identified-organization(3) certicom(132) curve(0)
-     *  }
-     */
+    /** Base OID: 1.3.132.0 */
     static final ASN1ObjectIdentifier ellipticCurve = new ASN1ObjectIdentifier("1.3.132.0");
 
+    /**  sect163k1 OID: 1.3.132.0.1 */
     static final ASN1ObjectIdentifier sect163k1 = ellipticCurve.branch("1");
+    /**  sect163r1 OID: 1.3.132.0.2 */
     static final ASN1ObjectIdentifier sect163r1 = ellipticCurve.branch("2");
+    /**  sect239k1 OID: 1.3.132.0.3 */
     static final ASN1ObjectIdentifier sect239k1 = ellipticCurve.branch("3");
+    /**  sect113r1 OID: 1.3.132.0.4 */
     static final ASN1ObjectIdentifier sect113r1 = ellipticCurve.branch("4");
+    /**  sect113r2 OID: 1.3.132.0.5 */
     static final ASN1ObjectIdentifier sect113r2 = ellipticCurve.branch("5");
+    /**  secp112r1 OID: 1.3.132.0.6 */
     static final ASN1ObjectIdentifier secp112r1 = ellipticCurve.branch("6");
+    /**  secp112r2 OID: 1.3.132.0.7 */
     static final ASN1ObjectIdentifier secp112r2 = ellipticCurve.branch("7");
+    /**  secp160r1 OID: 1.3.132.0.8 */
     static final ASN1ObjectIdentifier secp160r1 = ellipticCurve.branch("8");
+    /**  secp160k1 OID: 1.3.132.0.9 */
     static final ASN1ObjectIdentifier secp160k1 = ellipticCurve.branch("9");
+    /**  secp256k1 OID: 1.3.132.0.10 */
     static final ASN1ObjectIdentifier secp256k1 = ellipticCurve.branch("10");
+    /**  sect163r2 OID: 1.3.132.0.15 */
     static final ASN1ObjectIdentifier sect163r2 = ellipticCurve.branch("15");
+    /**  sect283k1 OID: 1.3.132.0.16 */
     static final ASN1ObjectIdentifier sect283k1 = ellipticCurve.branch("16");
+    /**  sect283r1 OID: 1.3.132.0.17 */
     static final ASN1ObjectIdentifier sect283r1 = ellipticCurve.branch("17");
+    /**  sect131r1 OID: 1.3.132.0.22 */
     static final ASN1ObjectIdentifier sect131r1 = ellipticCurve.branch("22");
+    /**  sect131r2 OID: 1.3.132.0.23 */
     static final ASN1ObjectIdentifier sect131r2 = ellipticCurve.branch("23");
+    /**  sect193r1 OID: 1.3.132.0.24 */
     static final ASN1ObjectIdentifier sect193r1 = ellipticCurve.branch("24");
+    /**  sect193r2 OID: 1.3.132.0.25 */
     static final ASN1ObjectIdentifier sect193r2 = ellipticCurve.branch("25");
+    /**  sect233k1 OID: 1.3.132.0.26 */
     static final ASN1ObjectIdentifier sect233k1 = ellipticCurve.branch("26");
+    /**  sect233r1 OID: 1.3.132.0.27 */
     static final ASN1ObjectIdentifier sect233r1 = ellipticCurve.branch("27");
+    /**  secp128r1 OID: 1.3.132.0.28 */
     static final ASN1ObjectIdentifier secp128r1 = ellipticCurve.branch("28");
+    /**  secp128r2 OID: 1.3.132.0.29 */
     static final ASN1ObjectIdentifier secp128r2 = ellipticCurve.branch("29");
+    /**  secp160r2 OID: 1.3.132.0.30 */
     static final ASN1ObjectIdentifier secp160r2 = ellipticCurve.branch("30");
+    /**  secp192k1 OID: 1.3.132.0.31 */
     static final ASN1ObjectIdentifier secp192k1 = ellipticCurve.branch("31");
+    /**  secp224k1 OID: 1.3.132.0.32 */
     static final ASN1ObjectIdentifier secp224k1 = ellipticCurve.branch("32");
+    /**  secp224r1 OID: 1.3.132.0.33 */
     static final ASN1ObjectIdentifier secp224r1 = ellipticCurve.branch("33");
+    /**  secp384r1 OID: 1.3.132.0.34 */
     static final ASN1ObjectIdentifier secp384r1 = ellipticCurve.branch("34");
+    /**  secp521r1 OID: 1.3.132.0.35 */
     static final ASN1ObjectIdentifier secp521r1 = ellipticCurve.branch("35");
+    /**  sect409k1 OID: 1.3.132.0.36 */
     static final ASN1ObjectIdentifier sect409k1 = ellipticCurve.branch("36");
+    /**  sect409r1 OID: 1.3.132.0.37 */
     static final ASN1ObjectIdentifier sect409r1 = ellipticCurve.branch("37");
+    /**  sect571k1 OID: 1.3.132.0.38 */
     static final ASN1ObjectIdentifier sect571k1 = ellipticCurve.branch("38");
+    /**  sect571r1 OID: 1.3.132.0.39 */
     static final ASN1ObjectIdentifier sect571r1 = ellipticCurve.branch("39");
 
+    /**  secp192r1 OID: 1.3.132.0.prime192v1 */
     static final ASN1ObjectIdentifier secp192r1 = X9ObjectIdentifiers.prime192v1;
+    /**  secp256r1 OID: 1.3.132.0.prime256v1 */
     static final ASN1ObjectIdentifier secp256r1 = X9ObjectIdentifiers.prime256v1;
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/sec/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/sec/package.html
deleted file mode 100644
index 5e34dec..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/sec/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for support of the SEC standard for Elliptic Curve.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/smime/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/smime/package.html
deleted file mode 100644
index d527aba..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/smime/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting S/MIME.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
index 17f0491..ba2f19e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTNamedCurves.java
@@ -17,14 +17,19 @@
  */
 public class TeleTrusTNamedCurves
 {
+    private static ECCurve configureCurve(ECCurve curve)
+    {
+        return curve;
+    }
+
     static X9ECParametersHolder brainpoolP160r1 = new X9ECParametersHolder()
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q
                 new BigInteger("340E7BE2A280EB74E2BE61BADA745D97E8F7C300", 16), // a
-                new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16)); // b
+                new BigInteger("1E589A8595423412134FAA2DBDEC95C8D8675E58", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -38,11 +43,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //   new BigInteger("24DBFF5DEC9B986BBFE5295A29BFBAE45E0F5D0B", 16), // Z
                 new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620F", 16), // q
                 new BigInteger("E95E4A5F737059DC60DFC7AD95B3D8139515620C", 16), // a'
-                new BigInteger("7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380", 16)); // b'
+                new BigInteger("7A556B6DAE535B7B51ED2C4D7DAA7A0B5C55F380", 16))); // b'
 
             return new X9ECParameters(
                 curve,
@@ -56,10 +61,10 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q
                 new BigInteger("6A91174076B1E0E19C39C031FE8685C1CAE040E5C69A28EF", 16), // a
-                new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16)); // b
+                new BigInteger("469A28EF7C28CCA3DC721D044F4496BCCA7EF4146FBF25C9", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -73,11 +78,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //new BigInteger("1B6F5CC8DB4DC7AF19458A9CB80DC2295E5EB9C3732104CB") //Z
                 new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86297", 16), // q
                 new BigInteger("C302F41D932A36CDA7A3463093D18DB78FCE476DE1A86294", 16), // a'
-                new BigInteger("13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79", 16)); // b'
+                new BigInteger("13D56FFAEC78681E68F9DEB43B35BEC2FB68542E27897B79", 16))); // b'
 
             return new X9ECParameters(
                 curve,
@@ -91,10 +96,10 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q
                 new BigInteger("68A5E62CA9CE6C1C299803A6C1530B514E182AD8B0042A59CAD29F43", 16), // a
-                new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16)); // b
+                new BigInteger("2580F63CCFE44138870713B1A92369E33E2135D266DBB372386C400B", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -107,11 +112,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //new BigInteger("2DF271E14427A346910CF7A2E6CFA7B3F484E5C2CCE1C8B730E28B3F") //Z
                 new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FF", 16), // q
                 new BigInteger("D7C134AA264366862A18302575D1D787B09F075797DA89F57EC8C0FC", 16), // a'
-                new BigInteger("4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D", 16)); // b'
+                new BigInteger("4B337D934104CD7BEF271BF60CED1ED20DA14C08B3BB64F18A60888D", 16))); // b'
 
             return new X9ECParameters(
                 curve,
@@ -124,10 +129,10 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q
                 new BigInteger("7D5A0975FC2C3057EEF67530417AFFE7FB8055C126DC5C6CE94A4B44F330B5D9", 16), // a
-                new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16)); // b
+                new BigInteger("26DC5C6CE94A4B44F330B5D9BBD77CBF958416295CF7E1CE6BCCDC18FF8C07B6", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -140,11 +145,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //new BigInteger("3E2D4BD9597B58639AE7AA669CAB9837CF5CF20A2C852D10F655668DFC150EF0") //Z
                 new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5377", 16), // q
                 new BigInteger("A9FB57DBA1EEA9BC3E660A909D838D726E3BF623D52620282013481D1F6E5374", 16), // a'
-                new BigInteger("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16)); // b'
+                new BigInteger("662C61C430D84EA4FE66A7733D0B76B7BF93EBC4AF2F49256AE58101FEE92B04", 16))); // b'
 
             return new X9ECParameters(
                 curve,
@@ -157,10 +162,10 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q
                 new BigInteger("3EE30B568FBAB0F883CCEBD46D3F3BB8A2A73513F5EB79DA66190EB085FFA9F492F375A97D860EB4", 16), // a
-                new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16)); // b
+                new BigInteger("520883949DFDBC42D3AD198640688A6FE13F41349554B49ACC31DCCD884539816F5EB4AC8FB1F1A6", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -173,11 +178,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //new BigInteger("15F75CAF668077F7E85B42EB01F0A81FF56ECD6191D55CB82B7D861458A18FEFC3E5AB7496F3C7B1") //Z
                 new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E27", 16), // q
                 new BigInteger("D35E472036BC4FB7E13C785ED201E065F98FCFA6F6F40DEF4F92B9EC7893EC28FCD412B1F1B32E24", 16), // a'
-                new BigInteger("A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CEB5B4FEF422340353", 16)); // b'
+                new BigInteger("A7F561E038EB1ED560B3D147DB782013064C19F27ED27C6780AAF77FB8A547CEB5B4FEF422340353", 16))); // b'
 
             return new X9ECParameters(
                 curve,
@@ -190,10 +195,10 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q
                 new BigInteger("7BC382C63D8C150C3C72080ACE05AFA0C2BEA28E4FB22787139165EFBA91F90F8AA5814A503AD4EB04A8C7DD22CE2826", 16), // a
-                new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16)); // b
+                new BigInteger("4A8C7DD22CE28268B39B55416F0447C2FB77DE107DCD2A62E880EA53EEB62D57CB4390295DBC9943AB78696FA504C11", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -206,11 +211,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //new BigInteger("41DFE8DD399331F7166A66076734A89CD0D2BCDB7D068E44E1F378F41ECBAE97D2D63DBC87BCCDDCCC5DA39E8589291C") //Z
                 new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC53", 16), // q
                 new BigInteger("8CB91E82A3386D280F5D6F7E50E641DF152F7109ED5456B412B1DA197FB71123ACD3A729901D1A71874700133107EC50", 16), // a'
-                new BigInteger("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16)); // b'
+                new BigInteger("7F519EADA7BDA81BD826DBA647910F8C4B9346ED8CCDC64E4B1ABD11756DCE1D2074AA263B88805CED70355A33B471EE", 16))); // b'
 
             return new X9ECParameters(
                 curve,
@@ -223,10 +228,10 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q
                 new BigInteger("7830A3318B603B89E2327145AC234CC594CBDD8D3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CA", 16), // a
-                new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16)); // b
+                new BigInteger("3DF91610A83441CAEA9863BC2DED5D5AA8253AA10A2EF1C98B9AC8B57F1117A72BF2C7B9E7C1AC4D77FC94CADC083E67984050B75EBAE5DD2809BD638016F723", 16))); // b
 
             return new X9ECParameters(
                 curve,
@@ -239,11 +244,11 @@
     {
         protected X9ECParameters createParameters()
         {
-            ECCurve curve = new ECCurve.Fp(
+            ECCurve curve = configureCurve(new ECCurve.Fp(
                 //new BigInteger("12EE58E6764838B69782136F0F2D3BA06E27695716054092E60A80BEDB212B64E585D90BCE13761F85C3F1D2A64E3BE8FEA2220F01EBA5EEB0F35DBD29D922AB") //Z
                 new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F3", 16), // q
                 new BigInteger("AADD9DB8DBE9C48B3FD4E6AE33C9FC07CB308DB3B3C9D20ED6639CCA703308717D4D9B009BC66842AECDA12AE6A380E62881FF2F2D82C68528AA6056583A48F0", 16), // a'
-                new BigInteger("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16)); // b'
+                new BigInteger("7CBBBCF9441CFAB76E1890E46884EAE321F70C0BCB4981527897504BEC3E36A62BCDFA2304976540F6450085F2DAE145C22553B465763689180EA2571867423E", 16))); // b'
 
             return new X9ECParameters(
                 curve,
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
index df9a0ff..895f5e8 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/TeleTrusTObjectIdentifiers.java
@@ -2,41 +2,74 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * TeleTrusT:
+ *   { iso(1) identifier-organization(3) teleTrust(36) algorithm(3)
+ *
+ */
 public interface TeleTrusTObjectIdentifiers
 {
+    /** 1.3.36.3 */
     static final ASN1ObjectIdentifier teleTrusTAlgorithm = new ASN1ObjectIdentifier("1.3.36.3");
 
+    /** 1.3.36.3.2.1 */
     static final ASN1ObjectIdentifier    ripemd160           = teleTrusTAlgorithm.branch("2.1");
+    /** 1.3.36.3.2.2 */
     static final ASN1ObjectIdentifier    ripemd128           = teleTrusTAlgorithm.branch("2.2");
+    /** 1.3.36.3.2.3 */
     static final ASN1ObjectIdentifier    ripemd256           = teleTrusTAlgorithm.branch("2.3");
 
+    /** 1.3.36.3.3.1 */
     static final ASN1ObjectIdentifier teleTrusTRSAsignatureAlgorithm = teleTrusTAlgorithm.branch("3.1");
 
-    static final ASN1ObjectIdentifier    rsaSignatureWithripemd160           = teleTrusTRSAsignatureAlgorithm.branch("2");
-    static final ASN1ObjectIdentifier    rsaSignatureWithripemd128           = teleTrusTRSAsignatureAlgorithm.branch("3");
-    static final ASN1ObjectIdentifier    rsaSignatureWithripemd256           = teleTrusTRSAsignatureAlgorithm.branch("4");
+    /** 1.3.36.3.3.1.2 */
+    static final ASN1ObjectIdentifier rsaSignatureWithripemd160      = teleTrusTRSAsignatureAlgorithm.branch("2");
+    /** 1.3.36.3.3.1.3 */
+    static final ASN1ObjectIdentifier rsaSignatureWithripemd128      = teleTrusTRSAsignatureAlgorithm.branch("3");
+    /** 1.3.36.3.3.1.4 */
+    static final ASN1ObjectIdentifier rsaSignatureWithripemd256      = teleTrusTRSAsignatureAlgorithm.branch("4");
 
-    static final ASN1ObjectIdentifier    ecSign = teleTrusTAlgorithm.branch("3.2");
+    /** 1.3.36.3.3.2 */
+    static final ASN1ObjectIdentifier    ecSign               = teleTrusTAlgorithm.branch("3.2");
 
-    static final ASN1ObjectIdentifier    ecSignWithSha1  = ecSign.branch("1");
+    /** 1.3.36.3.3.2,1 */
+    static final ASN1ObjectIdentifier    ecSignWithSha1       = ecSign.branch("1");
+    /** 1.3.36.3.3.2.2 */
     static final ASN1ObjectIdentifier    ecSignWithRipemd160  = ecSign.branch("2");
 
+    /** 1.3.36.3.3.2.8 */
     static final ASN1ObjectIdentifier ecc_brainpool = teleTrusTAlgorithm.branch("3.2.8");
+    /** 1.3.36.3.3.2.8.1 */
     static final ASN1ObjectIdentifier ellipticCurve = ecc_brainpool.branch("1");
+    /** 1.3.36.3.3.2.8.1 */
     static final ASN1ObjectIdentifier versionOne = ellipticCurve.branch("1");
 
+    /** 1.3.36.3.3.2.8.1.1 */
     static final ASN1ObjectIdentifier brainpoolP160r1 = versionOne.branch("1");
+    /** 1.3.36.3.3.2.8.1.2 */
     static final ASN1ObjectIdentifier brainpoolP160t1 = versionOne.branch("2");
+    /** 1.3.36.3.3.2.8.1.3 */
     static final ASN1ObjectIdentifier brainpoolP192r1 = versionOne.branch("3");
+    /** 1.3.36.3.3.2.8.1.4 */
     static final ASN1ObjectIdentifier brainpoolP192t1 = versionOne.branch("4");
+    /** 1.3.36.3.3.2.8.1.5 */
     static final ASN1ObjectIdentifier brainpoolP224r1 = versionOne.branch("5");
+    /** 1.3.36.3.3.2.8.1.6 */
     static final ASN1ObjectIdentifier brainpoolP224t1 = versionOne.branch("6");
+    /** 1.3.36.3.3.2.8.1.7 */
     static final ASN1ObjectIdentifier brainpoolP256r1 = versionOne.branch("7");
+    /** 1.3.36.3.3.2.8.1.8 */
     static final ASN1ObjectIdentifier brainpoolP256t1 = versionOne.branch("8");
+    /** 1.3.36.3.3.2.8.1.9 */
     static final ASN1ObjectIdentifier brainpoolP320r1 = versionOne.branch("9");
+    /** 1.3.36.3.3.2.8.1.10 */
     static final ASN1ObjectIdentifier brainpoolP320t1 = versionOne.branch("10");
+    /** 1.3.36.3.3.2.8.1.11 */
     static final ASN1ObjectIdentifier brainpoolP384r1 = versionOne.branch("11");
+    /** 1.3.36.3.3.2.8.1.12 */
     static final ASN1ObjectIdentifier brainpoolP384t1 = versionOne.branch("12");
+    /** 1.3.36.3.3.2.8.1.13 */
     static final ASN1ObjectIdentifier brainpoolP512r1 = versionOne.branch("13");
+    /** 1.3.36.3.3.2.8.1.14 */
     static final ASN1ObjectIdentifier brainpoolP512t1 = versionOne.branch("14");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/package.html
deleted file mode 100644
index 86606c3..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/teletrust/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes for TeleTrust related objects.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/tsp/package.html
deleted file mode 100644
index d6265f0..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/tsp/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting Time Stamp Protocol as described RFC 3161.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
index 353c196..312bacb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145NamedCurves.java
@@ -34,16 +34,16 @@
         curves[9] = new ECCurve.F2m(431, 1, 3, 5, ONE, new BigInteger("03CE10490F6A708FC26DFE8C3D27C4F94E690134D5BFF988D8D28AAEAEDE975936C66BAC536B18AE2DC312CA493117DAA469C640CAF3", 16));
 
         ECPoint[] points = new ECPoint[10];
-        points[0] = curves[0].createPoint(new BigInteger("2E2F85F5DD74CE983A5C4237229DAF8A3F35823BE", 16), new BigInteger("3826F008A8C51D7B95284D9D03FF0E00CE2CD723A", 16), false);
-        points[1] = curves[1].createPoint(new BigInteger("7A1F6653786A68192803910A3D30B2A2018B21CD54", 16), new BigInteger("5F49EB26781C0EC6B8909156D98ED435E45FD59918", 16), false);
-        points[2] = curves[2].createPoint(new BigInteger("4D41A619BCC6EADF0448FA22FAD567A9181D37389CA", 16), new BigInteger("10B51CC12849B234C75E6DD2028BF7FF5C1CE0D991A1", 16), false);
-        points[3] = curves[3].createPoint(new BigInteger("6BA06FE51464B2BD26DC57F48819BA9954667022C7D03", 16), new BigInteger("25FBC363582DCEC065080CA8287AAFF09788A66DC3A9E", 16), false);
-        points[4] = curves[4].createPoint(new BigInteger("714114B762F2FF4A7912A6D2AC58B9B5C2FCFE76DAEB7129", 16), new BigInteger("29C41E568B77C617EFE5902F11DB96FA9613CD8D03DB08DA", 16), false);
-        points[5] = curves[5].createPoint(new BigInteger("3FCDA526B6CDF83BA1118DF35B3C31761D3545F32728D003EEB25EFE96", 16), new BigInteger("9CA8B57A934C54DEEDA9E54A7BBAD95E3B2E91C54D32BE0B9DF96D8D35", 16), false);
-        points[6] = curves[6].createPoint(new BigInteger("02A29EF207D0E9B6C55CD260B306C7E007AC491CA1B10C62334A9E8DCD8D20FB7", 16), new BigInteger("10686D41FF744D4449FCCF6D8EEA03102E6812C93A9D60B978B702CF156D814EF", 16), false);
-        points[7] = curves[7].createPoint(new BigInteger("216EE8B189D291A0224984C1E92F1D16BF75CCD825A087A239B276D3167743C52C02D6E7232AA", 16), new BigInteger("5D9306BACD22B7FAEB09D2E049C6E2866C5D1677762A8F2F2DC9A11C7F7BE8340AB2237C7F2A0", 16), false);
-        points[8] = curves[8].createPoint(new BigInteger("324A6EDDD512F08C49A99AE0D3F961197A76413E7BE81A400CA681E09639B5FE12E59A109F78BF4A373541B3B9A1", 16), new BigInteger("1AB597A5B4477F59E39539007C7F977D1A567B92B043A49C6B61984C3FE3481AAF454CD41BA1F051626442B3C10", 16), false);
-        points[9] = curves[9].createPoint(new BigInteger("1A62BA79D98133A16BBAE7ED9A8E03C32E0824D57AEF72F88986874E5AAE49C27BED49A2A95058068426C2171E99FD3B43C5947C857D", 16), new BigInteger("70B5E1E14031C1F70BBEFE96BDDE66F451754B4CA5F48DA241F331AA396B8D1839A855C1769B1EA14BA53308B5E2723724E090E02DB9", 16), false);
+        points[0] = curves[0].createPoint(new BigInteger("2E2F85F5DD74CE983A5C4237229DAF8A3F35823BE", 16), new BigInteger("3826F008A8C51D7B95284D9D03FF0E00CE2CD723A", 16));
+        points[1] = curves[1].createPoint(new BigInteger("7A1F6653786A68192803910A3D30B2A2018B21CD54", 16), new BigInteger("5F49EB26781C0EC6B8909156D98ED435E45FD59918", 16));
+        points[2] = curves[2].createPoint(new BigInteger("4D41A619BCC6EADF0448FA22FAD567A9181D37389CA", 16), new BigInteger("10B51CC12849B234C75E6DD2028BF7FF5C1CE0D991A1", 16));
+        points[3] = curves[3].createPoint(new BigInteger("6BA06FE51464B2BD26DC57F48819BA9954667022C7D03", 16), new BigInteger("25FBC363582DCEC065080CA8287AAFF09788A66DC3A9E", 16));
+        points[4] = curves[4].createPoint(new BigInteger("714114B762F2FF4A7912A6D2AC58B9B5C2FCFE76DAEB7129", 16), new BigInteger("29C41E568B77C617EFE5902F11DB96FA9613CD8D03DB08DA", 16));
+        points[5] = curves[5].createPoint(new BigInteger("3FCDA526B6CDF83BA1118DF35B3C31761D3545F32728D003EEB25EFE96", 16), new BigInteger("9CA8B57A934C54DEEDA9E54A7BBAD95E3B2E91C54D32BE0B9DF96D8D35", 16));
+        points[6] = curves[6].createPoint(new BigInteger("02A29EF207D0E9B6C55CD260B306C7E007AC491CA1B10C62334A9E8DCD8D20FB7", 16), new BigInteger("10686D41FF744D4449FCCF6D8EEA03102E6812C93A9D60B978B702CF156D814EF", 16));
+        points[7] = curves[7].createPoint(new BigInteger("216EE8B189D291A0224984C1E92F1D16BF75CCD825A087A239B276D3167743C52C02D6E7232AA", 16), new BigInteger("5D9306BACD22B7FAEB09D2E049C6E2866C5D1677762A8F2F2DC9A11C7F7BE8340AB2237C7F2A0", 16));
+        points[8] = curves[8].createPoint(new BigInteger("324A6EDDD512F08C49A99AE0D3F961197A76413E7BE81A400CA681E09639B5FE12E59A109F78BF4A373541B3B9A1", 16), new BigInteger("1AB597A5B4477F59E39539007C7F977D1A567B92B043A49C6B61984C3FE3481AAF454CD41BA1F051626442B3C10", 16));
+        points[9] = curves[9].createPoint(new BigInteger("1A62BA79D98133A16BBAE7ED9A8E03C32E0824D57AEF72F88986874E5AAE49C27BED49A2A95058068426C2171E99FD3B43C5947C857D", 16), new BigInteger("70B5E1E14031C1F70BBEFE96BDDE66F451754B4CA5F48DA241F331AA396B8D1839A855C1769B1EA14BA53308B5E2723724E090E02DB9", 16));
 
         BigInteger[] n_s = new BigInteger[10];
         n_s[0] = new BigInteger("400000000000000000002BEC12BE2262D39BCF14D", 16);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java
index 0227d2a..8c16620 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/DSTU4145PointEncoder.java
@@ -3,7 +3,6 @@
 import java.math.BigInteger;
 import java.util.Random;
 
-import org.bouncycastle.asn1.x9.X9IntegerConverter;
 import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECFieldElement;
@@ -14,12 +13,8 @@
  * DSTU4145 encodes points somewhat differently than X9.62
  * It compresses the point to the size of the field element
  */
-
 public abstract class DSTU4145PointEncoder
 {
-
-    private static X9IntegerConverter converter = new X9IntegerConverter();
-
     private static BigInteger trace(ECFieldElement fe)
     {
         ECFieldElement t = fe;
@@ -38,26 +33,23 @@
      * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
      *         <code>null</code> if no solution exists.
      */
-    private static ECFieldElement solveQuadradicEquation(ECFieldElement beta)
+    private static ECFieldElement solveQuadraticEquation(ECCurve curve, ECFieldElement beta)
     {
-        ECFieldElement.F2m b = (ECFieldElement.F2m)beta;
-        ECFieldElement zeroElement = new ECFieldElement.F2m(
-            b.getM(), b.getK1(), b.getK2(), b.getK3(), ECConstants.ZERO);
-
-        if (beta.toBigInteger().equals(ECConstants.ZERO))
+        if (beta.isZero())
         {
-            return zeroElement;
+            return beta;
         }
 
+        ECFieldElement zeroElement = curve.fromBigInteger(ECConstants.ZERO);
+
         ECFieldElement z = null;
-        ECFieldElement gamma = zeroElement;
+        ECFieldElement gamma = null;
 
         Random rand = new Random();
-        int m = b.getM();
+        int m = beta.getFieldSize();
         do
         {
-            ECFieldElement t = new ECFieldElement.F2m(b.getM(), b.getK1(),
-                b.getK2(), b.getK3(), new BigInteger(m, rand));
+            ECFieldElement t = curve.fromBigInteger(new BigInteger(m, rand));
             z = zeroElement;
             ECFieldElement w = beta;
             for (int i = 1; i <= m - 1; i++)
@@ -66,13 +58,13 @@
                 z = z.square().add(w2.multiply(t));
                 w = w2.add(beta);
             }
-            if (!w.toBigInteger().equals(ECConstants.ZERO))
+            if (!w.isZero())
             {
                 return null;
             }
             gamma = z.square().add(z);
         }
-        while (gamma.toBigInteger().equals(ECConstants.ZERO));
+        while (gamma.isZero());
 
         return z;
     }
@@ -91,12 +83,15 @@
 
           return Arrays.copyOfRange(bytes, 1, bytes.length);*/
 
-        int byteCount = converter.getByteLength(Q.getX());
-        byte[] bytes = converter.integerToBytes(Q.getX().toBigInteger(), byteCount);
+        Q = Q.normalize();
 
-        if (!(Q.getX().toBigInteger().equals(ECConstants.ZERO)))
+        ECFieldElement x = Q.getAffineXCoord();
+
+        byte[] bytes = x.getEncoded();
+
+        if (!x.isZero())
         {
-            ECFieldElement y = Q.getY().multiply(Q.getX().invert());
+            ECFieldElement y = Q.getAffineYCoord().divide(x);
             if (trace(y).equals(ECConstants.ONE))
             {
                 bytes[bytes.length - 1] |= 0x01;
@@ -129,13 +124,12 @@
             bytes = Arrays.clone(bytes);
             bytes[bytes.length - 1] ^= 0x01;
         }
-        ECCurve.F2m c = (ECCurve.F2m)curve;
         ECFieldElement xp = curve.fromBigInteger(new BigInteger(1, bytes));
         ECFieldElement yp = null;
-        if (xp.toBigInteger().equals(ECConstants.ZERO))
+        if (xp.isZero())
         {
             yp = (ECFieldElement.F2m)curve.getB();
-            for (int i = 0; i < c.getM() - 1; i++)
+            for (int i = 0; i < curve.getFieldSize() - 1; i++)
             {
                 yp = yp.square();
             }
@@ -144,14 +138,14 @@
         {
             ECFieldElement beta = xp.add(curve.getA()).add(
                 curve.getB().multiply(xp.square().invert()));
-            ECFieldElement z = solveQuadradicEquation(beta);
+            ECFieldElement z = solveQuadraticEquation(curve, beta);
             if (z == null)
             {
                 throw new RuntimeException("Invalid point compression");
             }
             if (!trace(z).equals(k))
             {
-                z = z.add(curve.fromBigInteger(ECConstants.ONE));
+                z = z.addOne();
             }
             yp = xp.multiply(z);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
index 046bc6f..ccdb34e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/ua/UAObjectIdentifiers.java
@@ -2,15 +2,22 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * Ukrainian object identifiers
+ * <p>
+ * {iso(1) member-body(2) Ukraine(804) root(2) security(1) cryptography(1) pki(1)}
+ * <p>
+ * { ...  pki-alg(1) pki-alg-sym(3) Dstu4145WithGost34311(1) PB(1)}
+ * <p>
+ * DSTU4145 in polynomial basis has 2 oids, one for little-endian representation and one for big-endian
+ */
 public interface UAObjectIdentifiers
 {
-    // Ukrainian object identifiers
-    // {iso(1) member-body(2) Ukraine(804 ) root(2) security(1) cryptography(1) pki(1)}
-
+    /** Base OID: 1.2.804.2.1.1.1 */
     static final ASN1ObjectIdentifier UaOid = new ASN1ObjectIdentifier("1.2.804.2.1.1.1");
 
-    // {pki-alg(1) pki-alg-�sym(3) Dstu4145WithGost34311(1) PB(1)}
-    // DSTU4145 in polynomial basis has 2 oids, one for little-endian representation and one for big-endian
+    /** DSTU4145 Little Endian presentation.  OID: 1.2.804.2.1.1.1.1.3.1.1 */
     static final ASN1ObjectIdentifier dstu4145le = UaOid.branch("1.3.1.1");
+    /** DSTU4145 Big Endian presentation.  OID: 1.2.804.2.1.1.1.1.3.1.1.1 */
     static final ASN1ObjectIdentifier dstu4145be = UaOid.branch("1.3.1.1.1.1");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/util/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/util/package.html
deleted file mode 100644
index 1db893d..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/util/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-An ASN.1 dump utility.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
index 714a32c..6842182 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/BCStyle.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.asn1.x500.style;
 
 import java.io.IOException;
+import java.util.Enumeration;
 import java.util.Hashtable;
 
 import org.bouncycastle.asn1.ASN1Encodable;
@@ -19,8 +20,6 @@
 public class BCStyle
     implements X500NameStyle
 {
-    public static final X500NameStyle INSTANCE = new BCStyle();
-
     /**
      * country code - StringType(SIZE(2))
      */
@@ -273,9 +272,18 @@
         DefaultLookUp.put("name", NAME);
     }
 
+    /**
+     * Singleton instance.
+     */
+    public static final X500NameStyle INSTANCE = new BCStyle();
+
+    protected final Hashtable defaultLookUp;
+    protected final Hashtable defaultSymbols;
+
     protected BCStyle()
     {
-
+        defaultSymbols = copyHashTable(DefaultSymbols);
+        defaultLookUp = copyHashTable(DefaultLookUp);
     }
     
     public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
@@ -322,12 +330,12 @@
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
     {
-        return IETFUtils.findAttrNamesForOID(oid, DefaultLookUp);
+        return IETFUtils.findAttrNamesForOID(oid, defaultLookUp);
     }
 
     public ASN1ObjectIdentifier attrNameToOID(String attrName)
     {
-        return IETFUtils.decodeAttrName(attrName, DefaultLookUp);
+        return IETFUtils.decodeAttrName(attrName, defaultLookUp);
     }
 
     public boolean areEqual(X500Name name1, X500Name name2)
@@ -451,9 +459,23 @@
                 buf.append(',');
             }
 
-            IETFUtils.appendRDN(buf, rdns[i], DefaultSymbols);
+            IETFUtils.appendRDN(buf, rdns[i], defaultSymbols);
         }
 
         return buf.toString();
     }
+
+    private static Hashtable copyHashTable(Hashtable paramsMap)
+    {
+        Hashtable newTable = new Hashtable();
+
+        Enumeration keys = paramsMap.keys();
+        while (keys.hasMoreElements())
+        {
+            Object key = keys.nextElement();
+            newTable.put(key, paramsMap.get(key));
+        }
+
+        return newTable;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
index c73107e..b4f1794 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/IETFUtils.java
@@ -405,7 +405,7 @@
         int start = 0;
         if (vBuf.length() > 0)
         {
-            while (vBuf.charAt(start) == ' ')
+            while (vBuf.length() > start && vBuf.charAt(start) == ' ')
             {
                 vBuf.insert(start, "\\");
                 start += 2;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
index 8486989..8c92257 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x500/style/RFC4519Style.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.asn1.x500.style;
 
 import java.io.IOException;
+import java.util.Enumeration;
 import java.util.Hashtable;
 
 import org.bouncycastle.asn1.ASN1Encodable;
@@ -16,8 +17,6 @@
 public class RFC4519Style
     implements X500NameStyle
 {
-    public static final X500NameStyle INSTANCE = new RFC4519Style();
-
     public static final ASN1ObjectIdentifier businessCategory = new ASN1ObjectIdentifier("2.5.4.15");
     public static final ASN1ObjectIdentifier c = new ASN1ObjectIdentifier("2.5.4.6");
     public static final ASN1ObjectIdentifier cn = new ASN1ObjectIdentifier("2.5.4.3");
@@ -166,9 +165,18 @@
         // TODO: need to add correct matching for equality comparisons.
     }
 
+    /**
+     * Singleton instance.
+     */
+    public static final X500NameStyle INSTANCE = new RFC4519Style();
+
+    protected final Hashtable defaultLookUp;
+    protected final Hashtable defaultSymbols;
+
     protected RFC4519Style()
     {
-
+        defaultSymbols = copyHashTable(DefaultSymbols);
+        defaultLookUp = copyHashTable(DefaultLookUp);
     }
 
     public ASN1Encodable stringToValue(ASN1ObjectIdentifier oid, String value)
@@ -211,12 +219,12 @@
 
     public String[] oidToAttrNames(ASN1ObjectIdentifier oid)
     {
-        return IETFUtils.findAttrNamesForOID(oid, DefaultLookUp);
+        return IETFUtils.findAttrNamesForOID(oid, defaultLookUp);
     }
 
     public ASN1ObjectIdentifier attrNameToOID(String attrName)
     {
-        return IETFUtils.decodeAttrName(attrName, DefaultLookUp);
+        return IETFUtils.decodeAttrName(attrName, defaultLookUp);
     }
 
     public boolean areEqual(X500Name name1, X500Name name2)
@@ -350,9 +358,23 @@
                 buf.append(',');
             }
 
-            IETFUtils.appendRDN(buf, rdns[i], DefaultSymbols);
+            IETFUtils.appendRDN(buf, rdns[i], defaultSymbols);
         }
 
         return buf.toString();
     }
+
+    private static Hashtable copyHashTable(Hashtable paramsMap)
+    {
+        Hashtable newTable = new Hashtable();
+
+        Enumeration keys = paramsMap.keys();
+        while (keys.hasMoreElements())
+        {
+            Object key = keys.nextElement();
+            newTable.put(key, paramsMap.get(key));
+        }
+
+        return newTable;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
index 92aa0f7..73fe7b4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificate.java
@@ -41,7 +41,10 @@
         this.signatureAlgorithm = signatureAlgorithm;
         this.signatureValue = signatureValue;
     }
-    
+
+    /**
+     * @deprecated use getInstance() method.
+     */
     public AttributeCertificate(
         ASN1Sequence    seq)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
index 7b9d450..ae539f4 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/AttributeCertificateInfo.java
@@ -13,7 +13,7 @@
 public class AttributeCertificateInfo
     extends ASN1Object
 {
-    private ASN1Integer              version;
+    private ASN1Integer             version;
     private Holder                  holder;
     private AttCertIssuer           issuer;
     private AlgorithmIdentifier     signature;
@@ -48,22 +48,33 @@
     private AttributeCertificateInfo(
         ASN1Sequence   seq)
     {
-        if (seq.size() < 7 || seq.size() > 9)
+        if (seq.size() < 6 || seq.size() > 9)
         {
             throw new IllegalArgumentException("Bad sequence size: " + seq.size());
         }
 
-        this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
-        this.holder = Holder.getInstance(seq.getObjectAt(1));
-        this.issuer = AttCertIssuer.getInstance(seq.getObjectAt(2));
-        this.signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(3));
-        this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(4));
-        this.attrCertValidityPeriod = AttCertValidityPeriod.getInstance(seq.getObjectAt(5));
-        this.attributes = ASN1Sequence.getInstance(seq.getObjectAt(6));
-        
-        for (int i = 7; i < seq.size(); i++)
+        int start;
+        if (seq.getObjectAt(0) instanceof ASN1Integer)   // in version 1 certs version is DEFAULT  v1(0)
         {
-            ASN1Encodable    obj = (ASN1Encodable)seq.getObjectAt(i);
+            this.version = ASN1Integer.getInstance(seq.getObjectAt(0));
+            start = 1;
+        }
+        else
+        {
+            this.version = new ASN1Integer(0);
+            start = 0;
+        }
+
+        this.holder = Holder.getInstance(seq.getObjectAt(start));
+        this.issuer = AttCertIssuer.getInstance(seq.getObjectAt(start + 1));
+        this.signature = AlgorithmIdentifier.getInstance(seq.getObjectAt(start + 2));
+        this.serialNumber = ASN1Integer.getInstance(seq.getObjectAt(start + 3));
+        this.attrCertValidityPeriod = AttCertValidityPeriod.getInstance(seq.getObjectAt(start + 4));
+        this.attributes = ASN1Sequence.getInstance(seq.getObjectAt(start + 5));
+        
+        for (int i = start + 6; i < seq.size(); i++)
+        {
+            ASN1Encodable    obj = seq.getObjectAt(i);
 
             if (obj instanceof DERBitString)
             {
@@ -143,7 +154,10 @@
     {
         ASN1EncodableVector  v = new ASN1EncodableVector();
 
-        v.add(version);
+        if (version.getValue().intValue() != 0)
+        {
+            v.add(version);
+        }
         v.add(holder);
         v.add(issuer);
         v.add(signature);
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
index 91a37ad..61d7d4a 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificateList.java
@@ -31,6 +31,8 @@
     TBSCertList            tbsCertList;
     AlgorithmIdentifier    sigAlgId;
     DERBitString           sig;
+    boolean                isHashCodeSet = false;
+    int                    hashCodeValue;
 
     public static CertificateList getInstance(
         ASN1TaggedObject obj,
@@ -54,6 +56,10 @@
         return null;
     }
 
+    /**
+     * @deprecated use getInstance() method.
+     * @param seq
+     */
     public CertificateList(
         ASN1Sequence seq)
     {
@@ -124,4 +130,15 @@
 
         return new DERSequence(v);
     }
+
+    public int hashCode()
+    {
+        if (!isHashCodeSet)
+        {
+            hashCodeValue = super.hashCode();
+            isHashCodeSet = true;
+        }
+
+        return hashCodeValue;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
index e42cefa..4d7fc0b 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/CertificatePolicies.java
@@ -1,6 +1,7 @@
 package org.bouncycastle.asn1.x509;
 
 import org.bouncycastle.asn1.ASN1Object;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.ASN1TaggedObject;
@@ -35,6 +36,17 @@
     }
 
     /**
+     * Retrieve a CertificatePolicies for a passed in Extensions object, if present.
+     *
+     * @param extensions the extensions object to be examined.
+     * @return  the CertificatePolicies, null if the extension is not present.
+     */
+    public static CertificatePolicies fromExtensions(Extensions extensions)
+    {
+        return CertificatePolicies.getInstance(extensions.getExtensionParsedValue(Extension.certificatePolicies));
+    }
+
+    /**
      * Construct a CertificatePolicies object containing one PolicyInformation.
      * 
      * @param name the name to be contained.
@@ -71,6 +83,19 @@
         return tmp;
     }
 
+    public PolicyInformation getPolicyInformation(ASN1ObjectIdentifier policyIdentifier)
+    {
+        for (int i = 0; i != policyInformation.length; i++)
+        {
+            if (policyIdentifier.equals(policyInformation[i].getPolicyIdentifier()))
+            {
+                 return policyInformation[i];
+            }
+        }
+
+        return null;
+    }
+
     /**
      * Produce an object suitable for an ASN1OutputStream.
      * <pre>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
index dcc1b1f..84d21ca 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/ExtendedKeyUsage.java
@@ -25,6 +25,13 @@
     Hashtable     usageTable = new Hashtable();
     ASN1Sequence  seq;
 
+    /**
+     * Return an ExtendedKeyUsage from the passed in tagged object.
+     *
+     * @param obj the tagged object containing the ExtendedKeyUsage
+     * @param explicit true if the tagged object should be interpreted as explicitly tagged, false if implicit.
+     * @return the ExtendedKeyUsage contained.
+     */
     public static ExtendedKeyUsage getInstance(
         ASN1TaggedObject obj,
         boolean          explicit)
@@ -32,6 +39,12 @@
         return getInstance(ASN1Sequence.getInstance(obj, explicit));
     }
 
+    /**
+     * Return an ExtendedKeyUsage from the passed in object.
+     *
+     * @param obj an ExtendedKeyUsage, some form or encoding of one, or null.
+     * @return  an ExtendedKeyUsage object, or null if null is passed in.
+     */
     public static ExtendedKeyUsage getInstance(
         Object obj)
     {
@@ -47,11 +60,22 @@
         return null;
     }
 
+    /**
+     * Retrieve an ExtendedKeyUsage for a passed in Extensions object, if present.
+     *
+     * @param extensions the extensions object to be examined.
+     * @return  the ExtendedKeyUsage, null if the extension is not present.
+     */
     public static ExtendedKeyUsage fromExtensions(Extensions extensions)
     {
         return ExtendedKeyUsage.getInstance(extensions.getExtensionParsedValue(Extension.extendedKeyUsage));
     }
 
+    /**
+     * Base constructor, from a single KeyPurposeId.
+     *
+     * @param usage the keyPurposeId to be included.
+     */
     public ExtendedKeyUsage(
         KeyPurposeId  usage)
     {
@@ -78,6 +102,11 @@
         }
     }
 
+    /**
+     * Base constructor, from multiple KeyPurposeIds.
+     *
+     * @param usages an array of KeyPurposeIds.
+     */
     public ExtendedKeyUsage(
         KeyPurposeId[]  usages)
     {
@@ -103,7 +132,7 @@
 
         while (e.hasMoreElements())
         {
-            ASN1Primitive  o = (ASN1Primitive)e.nextElement();
+            KeyPurposeId  o = KeyPurposeId.getInstance(e.nextElement());
 
             v.add(o);
             this.usageTable.put(o, o);
@@ -112,6 +141,12 @@
         this.seq = new DERSequence(v);
     }
 
+    /**
+     * Return true if this ExtendedKeyUsage object contains the passed in keyPurposeId.
+     *
+     * @param keyPurposeId  the KeyPurposeId of interest.
+     * @return true if the keyPurposeId is present, false otherwise.
+     */
     public boolean hasKeyPurposeId(
         KeyPurposeId keyPurposeId)
     {
@@ -120,7 +155,7 @@
     
     /**
      * Returns all extended key usages.
-     * The returned vector contains DERObjectIdentifiers.
+     *
      * @return An array with all key purposes.
      */
     public KeyPurposeId[] getUsages()
@@ -135,11 +170,21 @@
         return temp;
     }
 
+    /**
+     * Return the number of KeyPurposeIds present in this ExtendedKeyUsage.
+     *
+     * @return the number of KeyPurposeIds
+     */
     public int size()
     {
         return usageTable.size();
     }
-    
+
+    /**
+     * Return the ASN.1 primitive form of this object.
+     *
+     * @return an ASN1Sequence.
+     */
     public ASN1Primitive toASN1Primitive()
     {
         return seq;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
index 6ae6e35..e854681 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/Holder.java
@@ -31,9 +31,9 @@
  * 
  * <pre>
  *         subject CHOICE {
- *          baseCertificateID [0] IssuerSerial,
+ *          baseCertificateID [0] EXPLICIT IssuerSerial,
  *          -- associated with a Public Key Certificate
- *          subjectName [1] GeneralNames },
+ *          subjectName [1] EXPLICIT GeneralNames },
  *          -- associated with a name
  * </pre>
  */
@@ -79,10 +79,10 @@
         switch (tagObj.getTagNo())
         {
         case 0:
-            baseCertificateID = IssuerSerial.getInstance(tagObj, false);
+            baseCertificateID = IssuerSerial.getInstance(tagObj, true);
             break;
         case 1:
-            entityName = GeneralNames.getInstance(tagObj, false);
+            entityName = GeneralNames.getInstance(tagObj, true);
             break;
         default:
             throw new IllegalArgumentException("unknown tag in Holder");
@@ -234,11 +234,11 @@
         {
             if (entityName != null)
             {
-                return new DERTaggedObject(false, 1, entityName);
+                return new DERTaggedObject(true, 1, entityName);
             }
             else
             {
-                return new DERTaggedObject(false, 0, baseCertificateID);
+                return new DERTaggedObject(true, 0, baseCertificateID);
             }
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
index 8d3036b..fefc939 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/IssuerSerial.java
@@ -10,6 +10,7 @@
 import org.bouncycastle.asn1.ASN1TaggedObject;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x500.X500Name;
 
 public class IssuerSerial
     extends ASN1Object
@@ -59,6 +60,13 @@
     }
 
     public IssuerSerial(
+        X500Name   issuer,
+        BigInteger serial)
+    {
+        this(new GeneralNames(new GeneralName(issuer)), new ASN1Integer(serial));
+    }
+
+    public IssuerSerial(
         GeneralNames    issuer,
         BigInteger serial)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
index 2943c0b..d4456b7 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/KeyUsage.java
@@ -74,6 +74,17 @@
         this.bitString = bitString;
     }
 
+    /**
+     * Return true if a given usage bit is set, false otherwise.
+     *
+     * @param usages combination of usage flags.
+     * @return true if all bits are set, false otherwise.
+     */
+    public boolean hasUsages(int usages)
+    {
+        return (bitString.intValue() & usages) == usages;
+    }
+
     public byte[] getBytes()
     {
         return bitString.getBytes();
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java
new file mode 100644
index 0000000..aeb53f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyConstraints.java
@@ -0,0 +1,106 @@
+package org.bouncycastle.asn1.x509;
+
+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.ASN1TaggedObject;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
+
+/**
+ * PKIX RFC 5280
+ * <pre>
+ * id-ce-policyConstraints OBJECT IDENTIFIER ::=  { id-ce 36 }
+ *
+ * PolicyConstraints ::= SEQUENCE {
+ *      requireExplicitPolicy           [0] SkipCerts OPTIONAL,
+ *      inhibitPolicyMapping            [1] SkipCerts OPTIONAL }
+ *
+ * SkipCerts ::= INTEGER (0..MAX)
+ * </pre>
+ */
+public class PolicyConstraints
+    extends ASN1Object
+{
+    private BigInteger requireExplicitPolicyMapping;
+    private BigInteger inhibitPolicyMapping;
+
+    public PolicyConstraints(BigInteger requireExplicitPolicyMapping, BigInteger inhibitPolicyMapping)
+    {
+        this.requireExplicitPolicyMapping = requireExplicitPolicyMapping;
+        this.inhibitPolicyMapping = inhibitPolicyMapping;
+    }
+
+    private PolicyConstraints(ASN1Sequence seq)
+    {
+        for (int i = 0; i != seq.size(); i++)
+        {
+            ASN1TaggedObject to = ASN1TaggedObject.getInstance(seq.getObjectAt(i));
+
+            if (to.getTagNo() == 0)
+            {
+                requireExplicitPolicyMapping = ASN1Integer.getInstance(to, false).getValue();
+            }
+            else if (to.getTagNo() == 1)
+            {
+                inhibitPolicyMapping = ASN1Integer.getInstance(to, false).getValue();
+            }
+            else
+            {
+                throw new IllegalArgumentException("Unknown tag encountered.");
+            }
+        }
+    }
+
+    public static PolicyConstraints getInstance(
+        Object  obj)
+    {
+        if (obj instanceof PolicyConstraints)
+        {
+            return (PolicyConstraints)obj;
+        }
+
+        if (obj != null)
+        {
+            return new PolicyConstraints(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
+    }
+
+    public static PolicyConstraints fromExtensions(Extensions extensions)
+    {
+        return PolicyConstraints.getInstance(extensions.getExtensionParsedValue(Extension.policyConstraints));
+    }
+
+    public BigInteger getRequireExplicitPolicyMapping()
+    {
+        return requireExplicitPolicyMapping;
+    }
+
+    public BigInteger getInhibitPolicyMapping()
+    {
+        return inhibitPolicyMapping;
+    }
+
+    public ASN1Primitive toASN1Primitive()
+    {
+        ASN1EncodableVector v = new ASN1EncodableVector();
+
+        if (requireExplicitPolicyMapping != null)
+        {
+            v.add(new DERTaggedObject(0, new ASN1Integer(requireExplicitPolicyMapping)));
+        }
+
+        if (inhibitPolicyMapping != null)
+        {
+            v.add(new DERTaggedObject(1, new ASN1Integer(inhibitPolicyMapping)));
+        }
+
+        return new DERSequence(v);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
index 295accf..fe09169 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/PolicyQualifierInfo.java
@@ -57,7 +57,8 @@
     * Creates a new <code>PolicyQualifierInfo</code> instance.
     *
     * @param as <code>PolicyQualifierInfo</code> X509 structure
-    * encoded as an ASN1Sequence. 
+    * encoded as an ASN1Sequence.
+    * @deprecated use PolicyQualifierInfo.getInstance()
     */
    public PolicyQualifierInfo(
        ASN1Sequence as)
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
index f020bcb..f29284d 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Extension.java
@@ -10,6 +10,7 @@
 
 /**
  * an object for the elements in the X.509 V3 extension block.
+ * @deprecated use Extension
  */
 public class X509Extension
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
index af2c9a9..ff7af8c 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509Name.java
@@ -1238,49 +1238,47 @@
 
         buf.append('=');
 
-        int     index = buf.length();
-        int     start = index;
-
+        int start = buf.length();
         buf.append(value);
-
-        int     end = buf.length();
+        int end = buf.length();
 
         if (value.length() >= 2 && value.charAt(0) == '\\' && value.charAt(1) == '#')
         {
-            index += 2;   
-        }
-
-        while (index != end)
-        {
-            if ((buf.charAt(index) == ',')
-               || (buf.charAt(index) == '"')
-               || (buf.charAt(index) == '\\')
-               || (buf.charAt(index) == '+')
-               || (buf.charAt(index) == '=')
-               || (buf.charAt(index) == '<')
-               || (buf.charAt(index) == '>')
-               || (buf.charAt(index) == ';'))
-            {
-                buf.insert(index, "\\");
-                index++;
-                end++;
-            }
-
-            index++;
-        }
-
-        while (buf.charAt(start) == ' ')
-        {
-            buf.insert(start, "\\");
             start += 2;
         }
 
-        int endBuf = buf.length() - 1;
-
-        while (endBuf >= 0 && buf.charAt(endBuf) == ' ')
+        while (start < end && buf.charAt(start) == ' ')
         {
-            buf.insert(endBuf, '\\');
-            endBuf--;
+            buf.insert(start, "\\");
+            start += 2;
+            ++end;
+        }
+
+        while (--end > start && buf.charAt(end) == ' ')
+        {
+            buf.insert(end, '\\');
+        }
+
+        while (start <= end)
+        {
+            switch (buf.charAt(start))
+            {
+            case ',':
+            case '"':
+            case '\\':
+            case '+':
+            case '=':
+            case '<':
+            case '>':
+            case ';':
+                buf.insert(start, "\\");
+                start += 2;
+                ++end;
+                break;
+            default:
+                ++start;
+                break;
+            }
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
index ed4dd32..e1c7a54 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/X509ObjectIdentifiers.java
@@ -4,64 +4,78 @@
 
 public interface X509ObjectIdentifiers
 {
-    //
-    // base id
-    //
-    static final String                 id                      = "2.5.4";
+    
+    /** Subject RDN components: commonName = 2.5.4.3 */
+    static final ASN1ObjectIdentifier    commonName              = new ASN1ObjectIdentifier("2.5.4.3");
+    /** Subject RDN components: countryName = 2.5.4.6 */
+    static final ASN1ObjectIdentifier    countryName             = new ASN1ObjectIdentifier("2.5.4.6");
+    /** Subject RDN components: localityName = 2.5.4.7 */
+    static final ASN1ObjectIdentifier    localityName            = new ASN1ObjectIdentifier("2.5.4.7");
+    /** Subject RDN components: stateOrProvinceName = 2.5.4.8 */
+    static final ASN1ObjectIdentifier    stateOrProvinceName     = new ASN1ObjectIdentifier("2.5.4.8");
+    /** Subject RDN components: organization = 2.5.4.10 */
+    static final ASN1ObjectIdentifier    organization            = new ASN1ObjectIdentifier("2.5.4.10");
+    /** Subject RDN components: organizationalUnitName = 2.5.4.11 */
+    static final ASN1ObjectIdentifier    organizationalUnitName  = new ASN1ObjectIdentifier("2.5.4.11");
 
-    static final ASN1ObjectIdentifier    commonName              = new ASN1ObjectIdentifier(id + ".3");
-    static final ASN1ObjectIdentifier    countryName             = new ASN1ObjectIdentifier(id + ".6");
-    static final ASN1ObjectIdentifier    localityName            = new ASN1ObjectIdentifier(id + ".7");
-    static final ASN1ObjectIdentifier    stateOrProvinceName     = new ASN1ObjectIdentifier(id + ".8");
-    static final ASN1ObjectIdentifier    organization            = new ASN1ObjectIdentifier(id + ".10");
-    static final ASN1ObjectIdentifier    organizationalUnitName  = new ASN1ObjectIdentifier(id + ".11");
-
+    /** Subject RDN components: telephone_number = 2.5.4.20 */
     static final ASN1ObjectIdentifier    id_at_telephoneNumber   = new ASN1ObjectIdentifier("2.5.4.20");
-    static final ASN1ObjectIdentifier    id_at_name              = new ASN1ObjectIdentifier(id + ".41");
+    /** Subject RDN components: name = 2.5.4.41 */
+    static final ASN1ObjectIdentifier    id_at_name              = new ASN1ObjectIdentifier("2.5.4.41");
 
-    // id-SHA1 OBJECT IDENTIFIER ::=    
-    //   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }    //
+    /**
+     * id-SHA1 OBJECT IDENTIFIER ::=    
+     *   {iso(1) identified-organization(3) oiw(14) secsig(3) algorithms(2) 26 }
+     * <p>
+     * OID: 1.3.14.3.2.27
+     */
     static final ASN1ObjectIdentifier    id_SHA1                 = new ASN1ObjectIdentifier("1.3.14.3.2.26");
 
-    //
-    // ripemd160 OBJECT IDENTIFIER ::=
-    //      {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)}
-    //
+    /**
+     * ripemd160 OBJECT IDENTIFIER ::=
+     *      {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) hashAlgorithm(2) RIPEMD-160(1)}
+     * <p>
+     * OID: 1.3.36.3.2.1
+     */
     static final ASN1ObjectIdentifier    ripemd160               = new ASN1ObjectIdentifier("1.3.36.3.2.1");
 
-    //
-    // ripemd160WithRSAEncryption OBJECT IDENTIFIER ::=
-    //      {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) }
-    //
+    /**
+     * ripemd160WithRSAEncryption OBJECT IDENTIFIER ::=
+     *      {iso(1) identified-organization(3) TeleTrust(36) algorithm(3) signatureAlgorithm(3) rsaSignature(1) rsaSignatureWithripemd160(2) }
+     * <p>
+     * OID: 1.3.36.3.3.1.2
+     */
     static final ASN1ObjectIdentifier    ripemd160WithRSAEncryption = new ASN1ObjectIdentifier("1.3.36.3.3.1.2");
 
 
+    /** OID: 2.5.8.1.1  */
     static final ASN1ObjectIdentifier    id_ea_rsa = new ASN1ObjectIdentifier("2.5.8.1.1");
     
-    // id-pkix
-    static final ASN1ObjectIdentifier id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
+    /** id-pkix OID: 1.3.6.1.5.5.7
+     */
+    static final ASN1ObjectIdentifier  id_pkix = new ASN1ObjectIdentifier("1.3.6.1.5.5.7");
 
-    //
-    // private internet extensions
-    //
-    static final ASN1ObjectIdentifier  id_pe = new ASN1ObjectIdentifier(id_pkix + ".1");
+    /**
+     * private internet extensions; OID = 1.3.6.1.5.5.7.1
+     */
+    static final ASN1ObjectIdentifier  id_pe   = id_pkix.branch("1");
 
-    //
-    // ISO ARC for standard certificate and CRL extensions
-    //
+    /**
+     * ISO ARC for standard certificate and CRL extensions
+     * <p>
+     * OID: 2.5.29
+     */
     static final ASN1ObjectIdentifier id_ce = new ASN1ObjectIdentifier("2.5.29");
 
-    //
-    // authority information access
-    //
-    static final ASN1ObjectIdentifier  id_ad = new ASN1ObjectIdentifier(id_pkix + ".48");
-    static final ASN1ObjectIdentifier  id_ad_caIssuers = new ASN1ObjectIdentifier(id_ad + ".2");
-    static final ASN1ObjectIdentifier  id_ad_ocsp = new ASN1ObjectIdentifier(id_ad + ".1");
+    /** id-pkix OID:         1.3.6.1.5.5.7.48  */
+    static final ASN1ObjectIdentifier  id_ad           = id_pkix.branch("48");
+    /** id-ad-caIssuers OID: 1.3.6.1.5.5.7.48.2  */
+    static final ASN1ObjectIdentifier  id_ad_caIssuers = id_ad.branch("2");
+    /** id-ad-ocsp OID:      1.3.6.1.5.5.7.48.1  */
+    static final ASN1ObjectIdentifier  id_ad_ocsp      = id_ad.branch("1");
 
-    //
-    //    OID for ocsp and crl uri in AuthorityInformationAccess extension
-    //
+    /** OID for ocsp uri in AuthorityInformationAccess extension */
     static final ASN1ObjectIdentifier ocspAccessMethod = id_ad_ocsp;
-    static final ASN1ObjectIdentifier crlAccessMethod = id_ad_caIssuers;
+    /** OID for crl uri in AuthorityInformationAccess extension */
+    static final ASN1ObjectIdentifier crlAccessMethod  = id_ad_caIssuers;
 }
-
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/x509/package.html
deleted file mode 100644
index 728921a..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and processing X.509 certificates.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
index 19ef12b..22db8cb 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/ETSIQCObjectIdentifiers.java
@@ -4,13 +4,8 @@
 
 public interface ETSIQCObjectIdentifiers
 {
-    //
-    // base id
-    //
-    static final ASN1ObjectIdentifier    id_etsi_qcs                  = new ASN1ObjectIdentifier("0.4.0.1862.1");
-
-    static final ASN1ObjectIdentifier    id_etsi_qcs_QcCompliance     = id_etsi_qcs.branch("1");
-    static final ASN1ObjectIdentifier    id_etsi_qcs_LimiteValue      = id_etsi_qcs.branch("2");
-    static final ASN1ObjectIdentifier    id_etsi_qcs_RetentionPeriod  = id_etsi_qcs.branch("3");
-    static final ASN1ObjectIdentifier    id_etsi_qcs_QcSSCD           = id_etsi_qcs.branch("4");
+    static final ASN1ObjectIdentifier    id_etsi_qcs_QcCompliance     = new ASN1ObjectIdentifier("0.4.0.1862.1.1");
+    static final ASN1ObjectIdentifier    id_etsi_qcs_LimiteValue      = new ASN1ObjectIdentifier("0.4.0.1862.1.2");
+    static final ASN1ObjectIdentifier    id_etsi_qcs_RetentionPeriod  = new ASN1ObjectIdentifier("0.4.0.1862.1.3");
+    static final ASN1ObjectIdentifier    id_etsi_qcs_QcSSCD           = new ASN1ObjectIdentifier("0.4.0.1862.1.4");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
index ecb5cce..0c840bd 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/RFC3739QCObjectIdentifiers.java
@@ -4,11 +4,8 @@
 
 public interface RFC3739QCObjectIdentifiers
 {
-    //
-    // base id
-    //
-    static final ASN1ObjectIdentifier   id_qcs             = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11");
-
-    static final ASN1ObjectIdentifier   id_qcs_pkixQCSyntax_v1  = id_qcs.branch("1");
-    static final ASN1ObjectIdentifier   id_qcs_pkixQCSyntax_v2  = id_qcs.branch("2");
+    /** OID: 1.3.6.1.5.5.7.11.1 */
+    static final ASN1ObjectIdentifier   id_qcs_pkixQCSyntax_v1  = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11.1");
+    /** OID: 1.3.6.1.5.5.7.11.2 */
+    static final ASN1ObjectIdentifier   id_qcs_pkixQCSyntax_v2  = new ASN1ObjectIdentifier("1.3.6.1.5.5.7.11.2");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/package.html
deleted file mode 100644
index 28cfef9..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/qualified/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and processing messages based around RFC3739
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
index 8cac124..d338614 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x509/sigi/SigIObjectIdentifiers.java
@@ -8,38 +8,53 @@
  */
 public interface SigIObjectIdentifiers
 {
+    /**
+     * OID: 1.3.36.8
+     */
     public final static ASN1ObjectIdentifier id_sigi = new ASN1ObjectIdentifier("1.3.36.8");
 
     /**
      * Key purpose IDs for German SigI (Signature Interoperability
      * Specification)
+     * <p>
+     * OID: 1.3.36.8.2
      */
-    public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier(id_sigi + ".2");
+    public final static ASN1ObjectIdentifier id_sigi_kp = new ASN1ObjectIdentifier("1.3.36.8.2");
 
     /**
      * Certificate policy IDs for German SigI (Signature Interoperability
      * Specification)
+     * <p>
+     * OID: 1.3.36.8.1
      */
-    public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier(id_sigi + ".1");
+    public final static ASN1ObjectIdentifier id_sigi_cp = new ASN1ObjectIdentifier("1.3.36.8.1");
 
     /**
      * Other Name IDs for German SigI (Signature Interoperability Specification)
+     * <p>
+     * OID: 1.3.36.8.4
      */
-    public final static ASN1ObjectIdentifier id_sigi_on = new ASN1ObjectIdentifier(id_sigi + ".4");
+    public final static ASN1ObjectIdentifier id_sigi_on = new ASN1ObjectIdentifier("1.3.36.8.4");
 
     /**
      * To be used for for the generation of directory service certificates.
+     * <p>
+     * OID: 1.3.36.8.2.1
      */
-    public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier(id_sigi_kp + ".1");
+    public static final ASN1ObjectIdentifier id_sigi_kp_directoryService = new ASN1ObjectIdentifier("1.3.36.8.2.1");
 
     /**
      * ID for PersonalData
+     * <p>
+     * OID: 1.3.36.8.4.1
      */
-    public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier(id_sigi_on + ".1");
+    public static final ASN1ObjectIdentifier id_sigi_on_personalData = new ASN1ObjectIdentifier("1.3.36.8.4.1");
 
     /**
-     * Certificate is conform to german signature law.
+     * Certificate is conformant to german signature law.
+     * <p>
+     * OID: 1.3.36.8.1.1
      */
-    public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier(id_sigi_cp + ".1");
+    public static final ASN1ObjectIdentifier id_sigi_cp_sigconform = new ASN1ObjectIdentifier("1.3.36.8.1.1");
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
index fb545c2..eeae0de 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/ECNamedCurveTable.java
@@ -65,6 +65,8 @@
             ecP = TeleTrusTNamedCurves.getByOID(oid);
         }
 
+        // NOTE: All the NIST curves are currently from SEC, so no point in redundant OID lookup
+
         return ecP;
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
index e059089..60f9008 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECParameters.java
@@ -39,11 +39,21 @@
         }
 
         X9Curve     x9c = new X9Curve(
-                        new X9FieldID((ASN1Sequence)seq.getObjectAt(1)),
-                        (ASN1Sequence)seq.getObjectAt(2));
+                        X9FieldID.getInstance(seq.getObjectAt(1)),
+                        ASN1Sequence.getInstance(seq.getObjectAt(2)));
 
         this.curve = x9c.getCurve();
-        this.g = new X9ECPoint(curve, (ASN1OctetString)seq.getObjectAt(3)).getPoint();
+        Object p = seq.getObjectAt(3);
+
+        if (p instanceof X9ECPoint)
+        {
+            this.g = ((X9ECPoint)p).getPoint();
+        }
+        else
+        {
+            this.g = new X9ECPoint(curve, (ASN1OctetString)p).getPoint();
+        }
+
         this.n = ((ASN1Integer)seq.getObjectAt(4)).getValue();
         this.seed = x9c.getSeed();
 
@@ -93,7 +103,7 @@
         byte[]      seed)
     {
         this.curve = curve;
-        this.g = g;
+        this.g = g.normalize();
         this.n = n;
         this.h = h;
         this.seed = seed;
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
index a4acb6e..cbb9116 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ECPoint.java
@@ -18,7 +18,7 @@
     public X9ECPoint(
         ECPoint p)
     {
-        this.p = p;
+        this.p = p.normalize();
     }
 
     public X9ECPoint(
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
index 30598e2..a210352 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9FieldID.java
@@ -71,11 +71,26 @@
         this.parameters = new DERSequence(fieldIdParams);
     }
 
-    public X9FieldID(
+    private X9FieldID(
         ASN1Sequence  seq)
     {
-        this.id = (ASN1ObjectIdentifier)seq.getObjectAt(0);
-        this.parameters = (ASN1Primitive)seq.getObjectAt(1);
+        this.id = ASN1ObjectIdentifier.getInstance(seq.getObjectAt(0));
+        this.parameters = seq.getObjectAt(1).toASN1Primitive();
+    }
+
+    public static X9FieldID getInstance(Object obj)
+    {
+        if (obj instanceof X9FieldID)
+        {
+            return (X9FieldID)obj;
+        }
+
+        if (obj != null)
+        {
+            return new X9FieldID(ASN1Sequence.getInstance(obj));
+        }
+
+        return null;
     }
 
     public ASN1ObjectIdentifier getIdentifier()
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
index f005cfa..eabf90e 100644
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/asn1/x9/X9ObjectIdentifiers.java
@@ -2,109 +2,172 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ *
+ * X9.62
+ * <pre>
+ * ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+ *                                    us(840) ansi-x962(10045) }
+ * </pre>
+ */
 public interface X9ObjectIdentifiers
 {
-    //
-    // X9.62
-    //
-    // ansi-X9-62 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-    //            us(840) ansi-x962(10045) }
-    //
+    /** Base OID: 1.2.840.10045 */
     static final ASN1ObjectIdentifier ansi_X9_62 = new ASN1ObjectIdentifier("1.2.840.10045");
+
+    /** OID: 1.2.840.10045.1 */
     static final ASN1ObjectIdentifier id_fieldType = ansi_X9_62.branch("1");
 
+    /** OID: 1.2.840.10045.1.1 */
     static final ASN1ObjectIdentifier prime_field = id_fieldType.branch("1");
 
+    /** OID: 1.2.840.10045.1.2 */
     static final ASN1ObjectIdentifier characteristic_two_field = id_fieldType.branch("2");
 
+    /** OID: 1.2.840.10045.1.2.3.1 */
     static final ASN1ObjectIdentifier gnBasis = characteristic_two_field.branch("3.1");
 
+    /** OID: 1.2.840.10045.1.2.3.2 */
     static final ASN1ObjectIdentifier tpBasis = characteristic_two_field.branch("3.2");
 
+    /** OID: 1.2.840.10045.1.2.3.3 */
     static final ASN1ObjectIdentifier ppBasis = characteristic_two_field.branch("3.3");
 
+    /** OID: 1.2.840.10045.4 */
     static final ASN1ObjectIdentifier id_ecSigType = ansi_X9_62.branch("4");
 
-    static final ASN1ObjectIdentifier ecdsa_with_SHA1 = new ASN1ObjectIdentifier(id_ecSigType + ".1");
+    /** OID: 1.2.840.10045.4.1 */
+    static final ASN1ObjectIdentifier ecdsa_with_SHA1 = id_ecSigType.branch("1");
 
+    /** OID: 1.2.840.10045.2 */
     static final ASN1ObjectIdentifier id_publicKeyType = ansi_X9_62.branch("2");
 
+    /** OID: 1.2.840.10045.2.1 */
     static final ASN1ObjectIdentifier id_ecPublicKey = id_publicKeyType.branch("1");
 
+    /** OID: 1.2.840.10045.4.3 */
     static final ASN1ObjectIdentifier ecdsa_with_SHA2 = id_ecSigType.branch("3");
 
+    /** OID: 1.2.840.10045.4.3.1 */
     static final ASN1ObjectIdentifier ecdsa_with_SHA224 = ecdsa_with_SHA2.branch("1");
 
+    /** OID: 1.2.840.10045.4.3.2 */
     static final ASN1ObjectIdentifier ecdsa_with_SHA256 = ecdsa_with_SHA2.branch("2");
 
+    /** OID: 1.2.840.10045.4.3.3 */
     static final ASN1ObjectIdentifier ecdsa_with_SHA384 = ecdsa_with_SHA2.branch("3");
 
+    /** OID: 1.2.840.10045.4.3.4 */
     static final ASN1ObjectIdentifier ecdsa_with_SHA512 = ecdsa_with_SHA2.branch("4");
 
-    //
-    // named curves
-    //
+    /**
+     * Named curves base
+     * <p>
+     * OID: 1.2.840.10045.1
+     */
     static final ASN1ObjectIdentifier ellipticCurve = ansi_X9_62.branch("3");
 
-    //
-    // Two Curves
-    //
+    /**
+     * Two Curves
+     * <p>
+     * OID: 1.2.840.10045.1.0
+     */
     static final ASN1ObjectIdentifier  cTwoCurve = ellipticCurve.branch("0");
 
+    /** Two Curve c2pnb163v1, OID: 1.2.840.10045.1.0.1 */
     static final ASN1ObjectIdentifier c2pnb163v1 = cTwoCurve.branch("1");
+    /** Two Curve c2pnb163v2, OID: 1.2.840.10045.1.0.2 */
     static final ASN1ObjectIdentifier c2pnb163v2 = cTwoCurve.branch("2");
+    /** Two Curve c2pnb163v3, OID: 1.2.840.10045.1.0.3 */
     static final ASN1ObjectIdentifier c2pnb163v3 = cTwoCurve.branch("3");
+    /** Two Curve c2pnb176w1, OID: 1.2.840.10045.1.0.4 */
     static final ASN1ObjectIdentifier c2pnb176w1 = cTwoCurve.branch("4");
+    /** Two Curve c2tnb191v1, OID: 1.2.840.10045.1.0.5 */
     static final ASN1ObjectIdentifier c2tnb191v1 = cTwoCurve.branch("5");
+    /** Two Curve c2tnb191v2, OID: 1.2.840.10045.1.0.6 */
     static final ASN1ObjectIdentifier c2tnb191v2 = cTwoCurve.branch("6");
+    /** Two Curve c2tnb191v3, OID: 1.2.840.10045.1.0.7 */
     static final ASN1ObjectIdentifier c2tnb191v3 = cTwoCurve.branch("7");
+    /** Two Curve c2onb191v4, OID: 1.2.840.10045.1.0.8 */
     static final ASN1ObjectIdentifier c2onb191v4 = cTwoCurve.branch("8");
+    /** Two Curve c2onb191v5, OID: 1.2.840.10045.1.0.9 */
     static final ASN1ObjectIdentifier c2onb191v5 = cTwoCurve.branch("9");
+    /** Two Curve c2pnb208w1, OID: 1.2.840.10045.1.0.10 */
     static final ASN1ObjectIdentifier c2pnb208w1 = cTwoCurve.branch("10");
+    /** Two Curve c2tnb239v1, OID: 1.2.840.10045.1.0.11 */
     static final ASN1ObjectIdentifier c2tnb239v1 = cTwoCurve.branch("11");
+    /** Two Curve c2tnb239v2, OID: 1.2.840.10045.1.0.12 */
     static final ASN1ObjectIdentifier c2tnb239v2 = cTwoCurve.branch("12");
+    /** Two Curve c2tnb239v3, OID: 1.2.840.10045.1.0.13 */
     static final ASN1ObjectIdentifier c2tnb239v3 = cTwoCurve.branch("13");
+    /** Two Curve c2onb239v4, OID: 1.2.840.10045.1.0.14 */
     static final ASN1ObjectIdentifier c2onb239v4 = cTwoCurve.branch("14");
+    /** Two Curve c2onb239v5, OID: 1.2.840.10045.1.0.15 */
     static final ASN1ObjectIdentifier c2onb239v5 = cTwoCurve.branch("15");
+    /** Two Curve c2pnb272w1, OID: 1.2.840.10045.1.0.16 */
     static final ASN1ObjectIdentifier c2pnb272w1 = cTwoCurve.branch("16");
+    /** Two Curve c2pnb304w1, OID: 1.2.840.10045.1.0.17 */
     static final ASN1ObjectIdentifier c2pnb304w1 = cTwoCurve.branch("17");
+    /** Two Curve c2tnb359v1, OID: 1.2.840.10045.1.0.18 */
     static final ASN1ObjectIdentifier c2tnb359v1 = cTwoCurve.branch("18");
+    /** Two Curve c2pnb368w1, OID: 1.2.840.10045.1.0.19 */
     static final ASN1ObjectIdentifier c2pnb368w1 = cTwoCurve.branch("19");
+    /** Two Curve c2tnb431r1, OID: 1.2.840.10045.1.0.20 */
     static final ASN1ObjectIdentifier c2tnb431r1 = cTwoCurve.branch("20");
 
-    //
-    // Prime
-    //
+    /**
+     * Prime Curves
+     * <p>
+     * OID: 1.2.840.10045.1.1
+     */
     static final ASN1ObjectIdentifier primeCurve = ellipticCurve.branch("1");
 
+    /** Prime Curve prime192v1, OID: 1.2.840.10045.1.1.1 */
     static final ASN1ObjectIdentifier prime192v1 = primeCurve.branch("1");
+    /** Prime Curve prime192v2, OID: 1.2.840.10045.1.1.2 */
     static final ASN1ObjectIdentifier prime192v2 = primeCurve.branch("2");
+    /** Prime Curve prime192v3, OID: 1.2.840.10045.1.1.3 */
     static final ASN1ObjectIdentifier prime192v3 = primeCurve.branch("3");
+    /** Prime Curve prime239v1, OID: 1.2.840.10045.1.1.4 */
     static final ASN1ObjectIdentifier prime239v1 = primeCurve.branch("4");
+    /** Prime Curve prime239v2, OID: 1.2.840.10045.1.1.5 */
     static final ASN1ObjectIdentifier prime239v2 = primeCurve.branch("5");
+    /** Prime Curve prime239v3, OID: 1.2.840.10045.1.1.6 */
     static final ASN1ObjectIdentifier prime239v3 = primeCurve.branch("6");
+    /** Prime Curve prime256v1, OID: 1.2.840.10045.1.1.7 */
     static final ASN1ObjectIdentifier prime256v1 = primeCurve.branch("7");
 
-    //
-    // DSA
-    //
-    // dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-    //            us(840) ansi-x957(10040) number-type(4) 1 }
+    /**
+     * DSA
+     * <pre>
+     * dsapublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2)
+     *                                         us(840) ansi-x957(10040) number-type(4) 1 }
+     * </pre>
+     * Base OID: 1.2.840.10040.4.1
+     */
     static final ASN1ObjectIdentifier id_dsa = new ASN1ObjectIdentifier("1.2.840.10040.4.1");
 
     /**
-     * id-dsa-with-sha1 OBJECT IDENTIFIER ::= { iso(1) member-body(2) us(840) x9-57
-     * (10040) x9cm(4) 3 }
+     * <pre>
+     * id-dsa-with-sha1 OBJECT IDENTIFIER ::= {
+     *     iso(1) member-body(2) us(840) x9-57(10040) x9cm(4) 3 }
+     * </pre>
+     * OID: 1.2.840.10040.4.3
      */
-    public static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3");
+    static final ASN1ObjectIdentifier id_dsa_with_sha1 = new ASN1ObjectIdentifier("1.2.840.10040.4.3");
 
     /**
-     * X9.63
+     * X9.63 - Signature Specification
+     * <p>
+     * Base OID: 1.3.133.16.840.63.0
      */
-    public static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0");
-    public static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme = x9_63_scheme.branch("2");
-    public static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3");
-    public static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme = x9_63_scheme.branch("16");
+    static final ASN1ObjectIdentifier x9_63_scheme = new ASN1ObjectIdentifier("1.3.133.16.840.63.0");
+    /** OID: 1.3.133.16.840.63.0.2 */
+    static final ASN1ObjectIdentifier dhSinglePass_stdDH_sha1kdf_scheme      = x9_63_scheme.branch("2");
+    /** OID: 1.3.133.16.840.63.0.3 */
+    static final ASN1ObjectIdentifier dhSinglePass_cofactorDH_sha1kdf_scheme = x9_63_scheme.branch("3");
+    /** OID: 1.3.133.16.840.63.0.16 */
+    static final ASN1ObjectIdentifier mqvSinglePass_sha1kdf_scheme           = x9_63_scheme.branch("16");
 
     /**
      * X9.42
@@ -112,21 +175,33 @@
 
     static final ASN1ObjectIdentifier ansi_X9_42 = new ASN1ObjectIdentifier("1.2.840.10046");
 
-    //
-    // Diffie-Hellman
-    //
-    // dhpublicnumber OBJECT IDENTIFIER ::= { iso(1) member-body(2)
-    //            us(840) ansi-x942(10046) number-type(2) 1 }
-    //
-    public static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1");
+    /**
+     * Diffie-Hellman
+     * <pre>
+     * dhpublicnumber OBJECT IDENTIFIER ::= {
+     *    iso(1) member-body(2)  us(840) ansi-x942(10046) number-type(2) 1
+     * }
+     * </pre>
+     * OID: 1.2.840.10046.2.1
+     */
+    static final ASN1ObjectIdentifier dhpublicnumber = ansi_X9_42.branch("2.1");
 
-    public static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3");
-    public static final ASN1ObjectIdentifier dhStatic = x9_42_schemes.branch("1");
-    public static final ASN1ObjectIdentifier dhEphem = x9_42_schemes.branch("2");
-    public static final ASN1ObjectIdentifier dhOneFlow = x9_42_schemes.branch("3");
-    public static final ASN1ObjectIdentifier dhHybrid1 = x9_42_schemes.branch("4");
-    public static final ASN1ObjectIdentifier dhHybrid2 = x9_42_schemes.branch("5");
-    public static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6");
-    public static final ASN1ObjectIdentifier mqv2 = x9_42_schemes.branch("7");
-    public static final ASN1ObjectIdentifier mqv1 = x9_42_schemes.branch("8");
+    /** X9.42 schemas base OID: 1.2.840.10046.3 */
+    static final ASN1ObjectIdentifier x9_42_schemes = ansi_X9_42.branch("3");
+    /** X9.42 dhStatic OID: 1.2.840.10046.3.1 */
+    static final ASN1ObjectIdentifier dhStatic        = x9_42_schemes.branch("1");
+    /** X9.42 dhEphem OID: 1.2.840.10046.3.2 */
+    static final ASN1ObjectIdentifier dhEphem         = x9_42_schemes.branch("2");
+    /** X9.42 dhOneFlow OID: 1.2.840.10046.3.3 */
+    static final ASN1ObjectIdentifier dhOneFlow       = x9_42_schemes.branch("3");
+    /** X9.42 dhHybrid1 OID: 1.2.840.10046.3.4 */
+    static final ASN1ObjectIdentifier dhHybrid1       = x9_42_schemes.branch("4");
+    /** X9.42 dhHybrid2 OID: 1.2.840.10046.3.5 */
+    static final ASN1ObjectIdentifier dhHybrid2       = x9_42_schemes.branch("5");
+    /** X9.42 dhHybridOneFlow OID: 1.2.840.10046.3.6 */
+    static final ASN1ObjectIdentifier dhHybridOneFlow = x9_42_schemes.branch("6");
+    /** X9.42 MQV2 OID: 1.2.840.10046.3.7 */
+    static final ASN1ObjectIdentifier mqv2            = x9_42_schemes.branch("7");
+    /** X9.42 MQV1 OID: 1.2.840.10046.3.8 */
+    static final ASN1ObjectIdentifier mqv1            = x9_42_schemes.branch("8");
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/asn1/x9/package.html b/bcprov/src/main/java/org/bouncycastle/asn1/x9/package.html
deleted file mode 100644
index 42fc97c..0000000
--- a/bcprov/src/main/java/org/bouncycastle/asn1/x9/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Support classes useful for encoding and supporting X9.62 elliptic curve.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
index bdb694d..dd056ac 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/BufferedBlockCipher.java
@@ -54,7 +54,7 @@
         }
         else
         {
-            partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
+            partialBlockOkay = (idx > 0 && (name.startsWith("CFB", idx) || name.startsWith("GCFB", idx) ||name.startsWith("OFB", idx) || name.startsWith("OpenPGP", idx) || name.startsWith("SIC", idx) || name.startsWith("GCTR", idx)));
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
index ef6e29e..0e2b4b0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DerivationFunction.java
@@ -7,11 +7,6 @@
 {
     public void init(DerivationParameters param);
 
-    /**
-     * return the message digest used as the basis for the function
-     */
-    public Digest getDigest();
-
     public int generateBytes(byte[] out, int outOff, int len)
         throws DataLengthException, IllegalArgumentException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java
new file mode 100644
index 0000000..180382d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/DigestDerivationFunction.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+/**
+ * base interface for general purpose Digest based byte derivation functions.
+ */
+public interface DigestDerivationFunction
+    extends DerivationFunction
+{
+    /**
+     * return the message digest used as the basis for the function
+     */
+    public Digest getDigest();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
new file mode 100644
index 0000000..16198ba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/MacDerivationFunction.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto;
+
+/**
+ * base interface for general purpose Mac based byte derivation functions.
+ */
+public interface MacDerivationFunction
+    extends DerivationFunction
+{
+    /**
+     * return the MAC used as the basis for the function
+     */
+    public Mac getMac();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
index 59944e0..a491b9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHBasicAgreement.java
@@ -42,10 +42,13 @@
         CipherParameters pubKey)
     {
         ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
-        ECPoint P = pub.getQ().multiply(key.getD());
+        ECPoint P = pub.getQ().multiply(key.getD()).normalize();
 
-        // if (p.isInfinity()) throw new RuntimeException("d*Q == infinity");
+        if (P.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid agreement value for ECDH");
+        }
 
-        return P.getX().toBigInteger();
+        return P.getAffineXCoord().toBigInteger();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
index 12b8405..28140df 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECDHCBasicAgreement.java
@@ -47,12 +47,18 @@
     public BigInteger calculateAgreement(
         CipherParameters pubKey)
     {
-        ECPublicKeyParameters   pub = (ECPublicKeyParameters)pubKey;
-        ECDomainParameters      params = pub.getParameters();
-        ECPoint P = pub.getQ().multiply(params.getH().multiply(key.getD()));
+        ECPublicKeyParameters pub = (ECPublicKeyParameters)pubKey;
+        ECDomainParameters params = pub.getParameters();
 
-        // if (p.isInfinity()) throw new RuntimeException("Invalid public key");
+        BigInteger hd = params.getH().multiply(key.getD()).mod(params.getN());
 
-        return P.getX().toBigInteger();
+        ECPoint P = pub.getQ().multiply(hd).normalize();
+
+        if (P.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid agreement value for ECDHC");
+        }
+
+        return P.getAffineXCoord().toBigInteger();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
index da88b4a..794f555 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/ECMQVBasicAgreement.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.crypto.params.MQVPublicParameters;
 import org.bouncycastle.math.ec.ECAlgorithms;
 import org.bouncycastle.math.ec.ECConstants;
+import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 
 public class ECMQVBasicAgreement
@@ -37,9 +38,14 @@
 
         ECPoint agreement = calculateMqvAgreement(staticPrivateKey.getParameters(), staticPrivateKey,
             privParams.getEphemeralPrivateKey(), privParams.getEphemeralPublicKey(),
-            pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey());
+            pubParams.getStaticPublicKey(), pubParams.getEphemeralPublicKey()).normalize();
 
-        return agreement.getX().toBigInteger();
+        if (agreement.isInfinity())
+        {
+            throw new IllegalStateException("Infinity is not a valid agreement value for MQV");
+        }
+
+        return agreement.getAffineXCoord().toBigInteger();
     }
 
     // The ECMQV Primitive as described in SEC-1, 3.4
@@ -55,37 +61,31 @@
         int e = (n.bitLength() + 1) / 2;
         BigInteger powE = ECConstants.ONE.shiftLeft(e);
 
-        // The Q2U public key is optional
-        ECPoint q;
-        if (Q2U == null)
-        {
-            q = parameters.getG().multiply(d2U.getD());
-        }
-        else
-        {
-            q = Q2U.getQ();
-        }
+        ECCurve curve = parameters.getCurve();
 
-        BigInteger x = q.getX().toBigInteger();
+        ECPoint[] points = new ECPoint[]{
+            // The Q2U public key is optional
+            ECAlgorithms.importPoint(curve, Q2U == null ? parameters.getG().multiply(d2U.getD()) : Q2U.getQ()),
+            ECAlgorithms.importPoint(curve, Q1V.getQ()),
+            ECAlgorithms.importPoint(curve, Q2V.getQ())
+        };
+
+        curve.normalizeAll(points);
+
+        ECPoint q2u = points[0], q1v = points[1], q2v = points[2];
+
+        BigInteger x = q2u.getAffineXCoord().toBigInteger();
         BigInteger xBar = x.mod(powE);
         BigInteger Q2UBar = xBar.setBit(e);
-        BigInteger s = d1U.getD().multiply(Q2UBar).mod(n).add(d2U.getD()).mod(n);
+        BigInteger s = d1U.getD().multiply(Q2UBar).add(d2U.getD()).mod(n);
 
-        BigInteger xPrime = Q2V.getQ().getX().toBigInteger();
+        BigInteger xPrime = q2v.getAffineXCoord().toBigInteger();
         BigInteger xPrimeBar = xPrime.mod(powE);
         BigInteger Q2VBar = xPrimeBar.setBit(e);
 
         BigInteger hs = parameters.getH().multiply(s).mod(n);
 
-//        ECPoint p = Q1V.getQ().multiply(Q2VBar).add(Q2V.getQ()).multiply(hs);
-        ECPoint p = ECAlgorithms.sumOfTwoMultiplies(
-            Q1V.getQ(), Q2VBar.multiply(hs).mod(n), Q2V.getQ(), hs);
-
-        if (p.isInfinity())
-        {
-            throw new IllegalStateException("Infinity is not a valid agreement value for MQV");
-        }
-
-        return p;
+        return ECAlgorithms.sumOfTwoMultiplies(
+            q1v, Q2VBar.multiply(hs).mod(n), q2v, hs);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html
deleted file mode 100644
index db47144..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/jpake/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Password Authenticated Key Exchange by Juggling (J-PAKE).
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
index 6803953..500b1dd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/kdf/ECDHKEKGenerator.java
@@ -11,9 +11,9 @@
 import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.DigestDerivationFunction;
 import org.bouncycastle.crypto.generators.KDF2BytesGenerator;
 import org.bouncycastle.crypto.params.KDFParameters;
 import org.bouncycastle.crypto.util.Pack;
@@ -22,9 +22,9 @@
  * X9.63 based key derivation function for ECDH CMS.
  */
 public class ECDHKEKGenerator
-    implements DerivationFunction
+    implements DigestDerivationFunction
 {
-    private DerivationFunction kdf;
+    private DigestDerivationFunction kdf;
 
     private ASN1ObjectIdentifier algorithm;
     private int                 keySize;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html
deleted file mode 100644
index 4b49331..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/agreement/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic key agreement classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java
new file mode 100644
index 0000000..3969fe8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/GeneralHashCommitter.java
@@ -0,0 +1,93 @@
+package org.bouncycastle.crypto.commitments;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Commitment;
+import org.bouncycastle.crypto.Committer;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A basic hash-committer based on the one described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking",
+ * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002).
+ * <p>
+ * The algorithm used by this class differs from the one given in that it includes the length of the message in the hash calculation.
+ * </p>
+ */
+public class GeneralHashCommitter
+    implements Committer
+{
+    private final Digest digest;
+    private final int byteLength;
+    private final SecureRandom random;
+
+    /**
+     * Base Constructor. The maximum message length that can be committed to is half the length of the internal
+     * block size for the digest (ExtendedDigest.getBlockLength()).
+     *
+     * @param digest digest to use for creating commitments.
+     * @param random source of randomness for generating secrets.
+     */
+    public GeneralHashCommitter(ExtendedDigest digest, SecureRandom random)
+    {
+        this.digest = digest;
+        this.byteLength = digest.getByteLength();
+        this.random = random;
+    }
+
+    /**
+     * Generate a commitment for the passed in message.
+     *
+     * @param message the message to be committed to,
+     * @return a Commitment
+     */
+    public Commitment commit(byte[] message)
+    {
+        if (message.length > byteLength / 2)
+        {
+            throw new DataLengthException("Message to be committed to too large for digest.");
+        }
+
+        byte[] w = new byte[byteLength - message.length];
+
+        random.nextBytes(w);
+
+        return new Commitment(w, calculateCommitment(w, message));
+    }
+
+    /**
+     * Return true if the passed in commitment represents a commitment to the passed in message.
+     *
+     * @param commitment a commitment previously generated.
+     * @param message the message that was expected to have been committed to.
+     * @return true if commitment matches message, false otherwise.
+     */
+    public boolean isRevealed(Commitment commitment, byte[] message)
+    {
+        if (message.length + commitment.getSecret().length != byteLength)
+        {
+            throw new DataLengthException("Message and witness secret lengths do not match.");
+        }
+
+        byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message);
+
+        return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment);
+    }
+
+    private byte[] calculateCommitment(byte[] w, byte[] message)
+    {
+        byte[] commitment = new byte[digest.getDigestSize()];
+
+        digest.update(w, 0, w.length);
+        digest.update(message, 0, message.length);
+
+        digest.update((byte)((message.length >>> 8)));
+        digest.update((byte)(message.length));
+
+        digest.doFinal(commitment, 0);
+
+        return commitment;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
index 1494c3c..b5860b5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/HashCommitter.java
@@ -12,6 +12,9 @@
 /**
  * A basic hash-committer as described in "Making Mix Nets Robust for Electronic Voting by Randomized Partial Checking",
  * by Jakobsson, Juels, and Rivest (11th Usenix Security Symposium, 2002).
+ * <p>
+ * Use this class if you can enforce fixed length for messages. If you need something more general, use the GeneralHashCommitter.
+ * </p>
  */
 public class HashCommitter
     implements Committer
@@ -55,7 +58,7 @@
     }
 
     /**
-     * Return true if the passed in commitment represents a commitment to the passed in maessage.
+     * Return true if the passed in commitment represents a commitment to the passed in message.
      *
      * @param commitment a commitment previously generated.
      * @param message the message that was expected to have been committed to.
@@ -63,6 +66,11 @@
      */
     public boolean isRevealed(Commitment commitment, byte[] message)
     {
+        if (message.length + commitment.getSecret().length != byteLength)
+        {
+            throw new DataLengthException("Message and witness secret lengths do not match.");
+        }
+
         byte[] calcCommitment = calculateCommitment(commitment.getSecret(), message);
 
         return Arrays.constantTimeAreEqual(commitment.getCommitment(), calcCommitment);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html
deleted file mode 100644
index 302cc60..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/commitments/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Commitment algorithms.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
new file mode 100644
index 0000000..55e579e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SM3Digest.java
@@ -0,0 +1,333 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.util.Pack;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of Chinese SM3 digest as described at
+ * http://tools.ietf.org/html/draft-shen-sm3-hash-00
+ * and at .... ( Chinese PDF )
+ * <p/>
+ * The specification says "process a bit stream",
+ * but this is written to process bytes in blocks of 4,
+ * meaning this will process 32-bit word groups.
+ * But so do also most other digest specifications,
+ * including the SHA-256 which was a origin for
+ * this specification.
+ */
+public class SM3Digest
+    extends GeneralDigest
+{
+    private static final int DIGEST_LENGTH = 32;   // bytes
+    private static final int BLOCK_SIZE = 64 / 4; // of 32 bit ints (16 ints)
+
+    private int[] V = new int[DIGEST_LENGTH / 4]; // in 32 bit ints (8 ints)
+    private int[] inwords = new int[BLOCK_SIZE];
+    private int xOff;
+
+    // Work-bufs used within processBlock()
+    private int[] W = new int[68];
+    private int[] W1 = new int[64];
+
+    // Round constant T for processBlock() which is 32 bit integer rolled left up to (63 MOD 32) bit positions.
+    private static final int[] T = new int[64];
+
+    static
+    {
+        for (int i = 0; i < 16; ++i)
+        {
+            int t = 0x79CC4519;
+            T[i] = (t << i) | (t >>> (32 - i));
+        }
+        for (int i = 16; i < 64; ++i)
+        {
+            int n = i % 32;
+            int t = 0x7A879D8A;
+            T[i] = (t << n) | (t >>> (32 - n));
+        }
+    }
+
+
+    /**
+     * Standard constructor
+     */
+    public SM3Digest()
+    {
+        reset();
+    }
+
+    /**
+     * Copy constructor.  This will copy the state of the provided
+     * message digest.
+     */
+    public SM3Digest(SM3Digest t)
+    {
+        super(t);
+
+        copyIn(t);
+    }
+
+    private void copyIn(SM3Digest t)
+    {
+        System.arraycopy(t.V, 0, this.V, 0, this.V.length);
+        System.arraycopy(t.inwords, 0, this.inwords, 0, this.inwords.length);
+        xOff = t.xOff;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "SM3";
+    }
+
+    public int getDigestSize()
+    {
+        return DIGEST_LENGTH;
+    }
+
+
+    public Memoable copy()
+    {
+        return new SM3Digest(this);
+    }
+
+    public void reset(Memoable other)
+    {
+        SM3Digest d = (SM3Digest)other;
+
+        super.copyIn(d);
+        copyIn(d);
+    }
+
+
+    /**
+     * reset the chaining variables
+     */
+    public void reset()
+    {
+        super.reset();
+
+        this.V[0] = 0x7380166F;
+        this.V[1] = 0x4914B2B9;
+        this.V[2] = 0x172442D7;
+        this.V[3] = 0xDA8A0600;
+        this.V[4] = 0xA96F30BC;
+        this.V[5] = 0x163138AA;
+        this.V[6] = 0xE38DEE4D;
+        this.V[7] = 0xB0FB0E4E;
+
+        this.xOff = 0;
+    }
+
+
+    public int doFinal(byte[] out,
+                       int outOff)
+    {
+        finish();
+
+        Pack.intToBigEndian(this.V[0], out, outOff + 0);
+        Pack.intToBigEndian(this.V[1], out, outOff + 4);
+        Pack.intToBigEndian(this.V[2], out, outOff + 8);
+        Pack.intToBigEndian(this.V[3], out, outOff + 12);
+        Pack.intToBigEndian(this.V[4], out, outOff + 16);
+        Pack.intToBigEndian(this.V[5], out, outOff + 20);
+        Pack.intToBigEndian(this.V[6], out, outOff + 24);
+        Pack.intToBigEndian(this.V[7], out, outOff + 28);
+
+        reset();
+
+        return DIGEST_LENGTH;
+    }
+
+
+    protected void processWord(byte[] in,
+                               int inOff)
+    {
+        // Note: Inlined for performance
+        // this.inwords[xOff] = Pack.bigEndianToInt(in, inOff);
+        int n = (((in[inOff] & 0xff) << 24) |
+            ((in[++inOff] & 0xff) << 16) |
+            ((in[++inOff] & 0xff) << 8) |
+            ((in[++inOff] & 0xff)));
+
+        this.inwords[this.xOff] = n;
+        ++this.xOff;
+
+        if (this.xOff >= 16)
+        {
+            processBlock();
+        }
+    }
+
+    protected void processLength(long bitLength)
+    {
+        if (this.xOff > (BLOCK_SIZE - 2))
+        {
+            // xOff == 15  --> can't fit the 64 bit length field at tail..
+            this.inwords[this.xOff] = 0; // fill with zero
+            ++this.xOff;
+
+            processBlock();
+        }
+        // Fill with zero words, until reach 2nd to last slot
+        while (this.xOff < (BLOCK_SIZE - 2))
+        {
+            this.inwords[this.xOff] = 0;
+            ++this.xOff;
+        }
+
+        // Store input data length in BITS
+        this.inwords[this.xOff++] = (int)(bitLength >>> 32);
+        this.inwords[this.xOff++] = (int)(bitLength);
+    }
+
+/*
+
+3.4.2.  Constants
+
+
+   Tj = 79cc4519        when 0  < = j < = 15
+   Tj = 7a879d8a        when 16 < = j < = 63
+
+3.4.3.  Boolean function
+
+
+   FFj(X;Y;Z) = X XOR Y XOR Z                       when 0  < = j < = 15
+              = (X AND Y) OR (X AND Z) OR (Y AND Z) when 16 < = j < = 63
+
+   GGj(X;Y;Z) = X XOR Y XOR Z                       when 0  < = j < = 15
+              = (X AND Y) OR (NOT X AND Z)          when 16 < = j < = 63
+
+   The X, Y, Z in the fomular are words!GBP
+
+3.4.4.  Permutation function
+
+
+   P0(X) = X XOR (X <<<  9) XOR (X <<< 17)   ## ROLL, not SHIFT
+   P1(X) = X XOR (X <<< 15) XOR (X <<< 23)   ## ROLL, not SHIFT
+
+   The X in the fomular are a word.
+
+----------
+
+Each ROLL converted to Java expression:
+
+ROLL 9  :  ((x <<  9) | (x >>> (32-9))))
+ROLL 17 :  ((x << 17) | (x >>> (32-17)))
+ROLL 15 :  ((x << 15) | (x >>> (32-15)))
+ROLL 23 :  ((x << 23) | (x >>> (32-23)))
+
+ */
+
+    private int P0(final int x)
+    {
+        final int r9 = ((x << 9) | (x >>> (32 - 9)));
+        final int r17 = ((x << 17) | (x >>> (32 - 17)));
+        return (x ^ r9 ^ r17);
+    }
+
+    private int P1(final int x)
+    {
+        final int r15 = ((x << 15) | (x >>> (32 - 15)));
+        final int r23 = ((x << 23) | (x >>> (32 - 23)));
+        return (x ^ r15 ^ r23);
+    }
+
+    private int FF0(final int x, final int y, final int z)
+    {
+        return (x ^ y ^ z);
+    }
+
+    private int FF1(final int x, final int y, final int z)
+    {
+        return ((x & y) | (x & z) | (y & z));
+    }
+
+    private int GG0(final int x, final int y, final int z)
+    {
+        return (x ^ y ^ z);
+    }
+
+    private int GG1(final int x, final int y, final int z)
+    {
+        return ((x & y) | ((~x) & z));
+    }
+
+
+    protected void processBlock()
+    {
+        for (int j = 0; j < 16; ++j)
+        {
+            this.W[j] = this.inwords[j];
+        }
+        for (int j = 16; j < 68; ++j)
+        {
+            int wj3 = this.W[j - 3];
+            int r15 = ((wj3 << 15) | (wj3 >>> (32 - 15)));
+            int wj13 = this.W[j - 13];
+            int r7 = ((wj13 << 7) | (wj13 >>> (32 - 7)));
+            this.W[j] = P1(this.W[j - 16] ^ this.W[j - 9] ^ r15) ^ r7 ^ this.W[j - 6];
+        }
+        for (int j = 0; j < 64; ++j)
+        {
+            this.W1[j] = this.W[j] ^ this.W[j + 4];
+        }
+
+        int A = this.V[0];
+        int B = this.V[1];
+        int C = this.V[2];
+        int D = this.V[3];
+        int E = this.V[4];
+        int F = this.V[5];
+        int G = this.V[6];
+        int H = this.V[7];
+
+
+        for (int j = 0; j < 16; ++j)
+        {
+            int a12 = ((A << 12) | (A >>> (32 - 12)));
+            int s1_ = a12 + E + T[j];
+            int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7)));
+            int SS2 = SS1 ^ a12;
+            int TT1 = FF0(A, B, C) + D + SS2 + this.W1[j];
+            int TT2 = GG0(E, F, G) + H + SS1 + this.W[j];
+            D = C;
+            C = ((B << 9) | (B >>> (32 - 9)));
+            B = A;
+            A = TT1;
+            H = G;
+            G = ((F << 19) | (F >>> (32 - 19)));
+            F = E;
+            E = P0(TT2);
+        }
+
+        // Different FF,GG functions on rounds 16..63
+        for (int j = 16; j < 64; ++j)
+        {
+            int a12 = ((A << 12) | (A >>> (32 - 12)));
+            int s1_ = a12 + E + T[j];
+            int SS1 = ((s1_ << 7) | (s1_ >>> (32 - 7)));
+            int SS2 = SS1 ^ a12;
+            int TT1 = FF1(A, B, C) + D + SS2 + this.W1[j];
+            int TT2 = GG1(E, F, G) + H + SS1 + this.W[j];
+            D = C;
+            C = ((B << 9) | (B >>> (32 - 9)));
+            B = A;
+            A = TT1;
+            H = G;
+            G = ((F << 19) | (F >>> (32 - 19)));
+            F = E;
+            E = P0(TT2);
+        }
+
+        this.V[0] ^= A;
+        this.V[1] ^= B;
+        this.V[2] ^= C;
+        this.V[3] ^= D;
+        this.V[4] ^= E;
+        this.V[5] ^= F;
+        this.V[6] ^= G;
+        this.V[7] ^= H;
+
+        this.xOff = 0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
new file mode 100644
index 0000000..06eaabd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinDigest.java
@@ -0,0 +1,116 @@
+package org.bouncycastle.crypto.digests;
+
+import org.bouncycastle.crypto.ExtendedDigest;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein parameterised hash function in 256, 512 and 1024 bit block sizes,
+ * based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p/>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p/>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ *
+ * @see SkeinEngine
+ * @see SkeinParameters
+ */
+public class SkeinDigest
+    implements ExtendedDigest, Memoable
+{
+    /**
+     * 256 bit block size - Skein-256
+     */
+    public static final int SKEIN_256 = SkeinEngine.SKEIN_256;
+    /**
+     * 512 bit block size - Skein-512
+     */
+    public static final int SKEIN_512 = SkeinEngine.SKEIN_512;
+    /**
+     * 1024 bit block size - Skein-1024
+     */
+    public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+    private SkeinEngine engine;
+
+    /**
+     * Constructs a Skein digest with an internal state size and output size.
+     *
+     * @param stateSizeBits  the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+     *                       {@link #SKEIN_1024}.
+     * @param digestSizeBits the output/digest size to produce in bits, which must be an integral number of
+     *                       bytes.
+     */
+    public SkeinDigest(int stateSizeBits, int digestSizeBits)
+    {
+        this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+        init(null);
+    }
+
+    public SkeinDigest(SkeinDigest digest)
+    {
+        this.engine = new SkeinEngine(digest.engine);
+    }
+
+    public void reset(Memoable other)
+    {
+        SkeinDigest d = (SkeinDigest)other;
+        engine.reset(d.engine);
+    }
+
+    public Memoable copy()
+    {
+        return new SkeinDigest(this);
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Skein-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8);
+    }
+
+    public int getDigestSize()
+    {
+        return engine.getOutputSize();
+    }
+
+    public int getByteLength()
+    {
+        return engine.getBlockSize();
+    }
+
+    /**
+     * Optionally initialises the Skein digest with the provided parameters.<br>
+     * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+     *
+     * @param params the parameters to apply to this engine, or <code>null</code> to use no parameters.
+     */
+    public void init(SkeinParameters params)
+    {
+        engine.init(params);
+    }
+
+    public void reset()
+    {
+        engine.reset();
+    }
+
+    public void update(byte in)
+    {
+        engine.update(in);
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        engine.update(in, inOff, len);
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        return engine.doFinal(out, outOff);
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
new file mode 100644
index 0000000..bca524e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/digests/SkeinEngine.java
@@ -0,0 +1,817 @@
+package org.bouncycastle.crypto.digests;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Memoable;
+
+/**
+ * Implementation of the Skein family of parameterised hash functions in 256, 512 and 1024 bit block
+ * sizes, based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p/>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p/>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ * This implementation is the basis for {@link SkeinDigest} and {@link SkeinMac}, implementing the
+ * parameter based configuration system that allows Skein to be adapted to multiple applications. <br>
+ * Initialising the engine with {@link SkeinParameters} allows standard and arbitrary parameters to
+ * be applied during the Skein hash function.
+ * <p/>
+ * Implemented:
+ * <ul>
+ * <li>256, 512 and 1024 bit internal states.</li>
+ * <li>Full 96 bit input length.</li>
+ * <li>Parameters defined in the Skein specification, and arbitrary other pre and post message
+ * parameters.</li>
+ * <li>Arbitrary output size in 1 byte intervals.</li>
+ * </ul>
+ * <p/>
+ * Not implemented:
+ * <ul>
+ * <li>Sub-byte length input (bit padding).</li>
+ * <li>Tree hashing.</li>
+ * </ul>
+ *
+ * @see SkeinParameters
+ */
+public class SkeinEngine
+    implements Memoable
+{
+    /**
+     * 256 bit block size - Skein 256
+     */
+    public static final int SKEIN_256 = ThreefishEngine.BLOCKSIZE_256;
+    /**
+     * 512 bit block size - Skein 512
+     */
+    public static final int SKEIN_512 = ThreefishEngine.BLOCKSIZE_512;
+    /**
+     * 1024 bit block size - Skein 1024
+     */
+    public static final int SKEIN_1024 = ThreefishEngine.BLOCKSIZE_1024;
+
+    // Minimal at present, but more complex when tree hashing is implemented
+    private static class Configuration
+    {
+        private byte[] bytes = new byte[32];
+
+        public Configuration(long outputSizeBits)
+        {
+            // 0..3 = ASCII SHA3
+            bytes[0] = (byte)'S';
+            bytes[1] = (byte)'H';
+            bytes[2] = (byte)'A';
+            bytes[3] = (byte)'3';
+
+            // 4..5 = version number in LSB order
+            bytes[4] = 1;
+            bytes[5] = 0;
+
+            // 8..15 = output length
+            ThreefishEngine.wordToBytes(outputSizeBits, bytes, 8);
+        }
+
+        public byte[] getBytes()
+        {
+            return bytes;
+        }
+
+    }
+
+    public static class Parameter
+    {
+        private int type;
+        private byte[] value;
+
+        public Parameter(int type, byte[] value)
+        {
+            this.type = type;
+            this.value = value;
+        }
+
+        public int getType()
+        {
+            return type;
+        }
+
+        public byte[] getValue()
+        {
+            return value;
+        }
+
+    }
+
+    /**
+     * The parameter type for the Skein key.
+     */
+    private static final int PARAM_TYPE_KEY = 0;
+
+    /**
+     * The parameter type for the Skein configuration block.
+     */
+    private static final int PARAM_TYPE_CONFIG = 4;
+
+    /**
+     * The parameter type for the message.
+     */
+    private static final int PARAM_TYPE_MESSAGE = 48;
+
+    /**
+     * The parameter type for the output transformation.
+     */
+    private static final int PARAM_TYPE_OUTPUT = 63;
+
+    /**
+     * Precalculated UBI(CFG) states for common state/output combinations without key or other
+     * pre-message params.
+     */
+    private static final Hashtable INITIAL_STATES = new Hashtable();
+
+    static
+    {
+        // From Appendix C of the Skein 1.3 NIST submission
+        initialState(SKEIN_256, 128, new long[]{
+            0xe1111906964d7260L,
+            0x883daaa77c8d811cL,
+            0x10080df491960f7aL,
+            0xccf7dde5b45bc1c2L});
+
+        initialState(SKEIN_256, 160, new long[]{
+            0x1420231472825e98L,
+            0x2ac4e9a25a77e590L,
+            0xd47a58568838d63eL,
+            0x2dd2e4968586ab7dL});
+
+        initialState(SKEIN_256, 224, new long[]{
+            0xc6098a8c9ae5ea0bL,
+            0x876d568608c5191cL,
+            0x99cb88d7d7f53884L,
+            0x384bddb1aeddb5deL});
+
+        initialState(SKEIN_256, 256, new long[]{
+            0xfc9da860d048b449L,
+            0x2fca66479fa7d833L,
+            0xb33bc3896656840fL,
+            0x6a54e920fde8da69L});
+
+        initialState(SKEIN_512, 128, new long[]{
+            0xa8bc7bf36fbf9f52L,
+            0x1e9872cebd1af0aaL,
+            0x309b1790b32190d3L,
+            0xbcfbb8543f94805cL,
+            0x0da61bcd6e31b11bL,
+            0x1a18ebead46a32e3L,
+            0xa2cc5b18ce84aa82L,
+            0x6982ab289d46982dL});
+
+        initialState(SKEIN_512, 160, new long[]{
+            0x28b81a2ae013bd91L,
+            0xc2f11668b5bdf78fL,
+            0x1760d8f3f6a56f12L,
+            0x4fb747588239904fL,
+            0x21ede07f7eaf5056L,
+            0xd908922e63ed70b8L,
+            0xb8ec76ffeccb52faL,
+            0x01a47bb8a3f27a6eL});
+
+        initialState(SKEIN_512, 224, new long[]{
+            0xccd0616248677224L,
+            0xcba65cf3a92339efL,
+            0x8ccd69d652ff4b64L,
+            0x398aed7b3ab890b4L,
+            0x0f59d1b1457d2bd0L,
+            0x6776fe6575d4eb3dL,
+            0x99fbc70e997413e9L,
+            0x9e2cfccfe1c41ef7L});
+
+        initialState(SKEIN_512, 384, new long[]{
+            0xa3f6c6bf3a75ef5fL,
+            0xb0fef9ccfd84faa4L,
+            0x9d77dd663d770cfeL,
+            0xd798cbf3b468fddaL,
+            0x1bc4a6668a0e4465L,
+            0x7ed7d434e5807407L,
+            0x548fc1acd4ec44d6L,
+            0x266e17546aa18ff8L});
+
+        initialState(SKEIN_512, 512, new long[]{
+            0x4903adff749c51ceL,
+            0x0d95de399746df03L,
+            0x8fd1934127c79bceL,
+            0x9a255629ff352cb1L,
+            0x5db62599df6ca7b0L,
+            0xeabe394ca9d5c3f4L,
+            0x991112c71a75b523L,
+            0xae18a40b660fcc33L});
+    }
+
+    private static void initialState(int blockSize, int outputSize, long[] state)
+    {
+        INITIAL_STATES.put(variantIdentifier(blockSize / 8, outputSize / 8), state);
+    }
+
+    private static Integer variantIdentifier(int blockSizeBytes, int outputSizeBytes)
+    {
+        return new Integer((outputSizeBytes << 16) | blockSizeBytes);
+    }
+
+    private static class UbiTweak
+    {
+        /**
+         * Point at which position might overflow long, so switch to add with carry logic
+         */
+        private static final long LOW_RANGE = Long.MAX_VALUE - Integer.MAX_VALUE;
+
+        /**
+         * Bit 127 = final
+         */
+        private static final long T1_FINAL = 1L << 63;
+
+        /**
+         * Bit 126 = first
+         */
+        private static final long T1_FIRST = 1L << 62;
+
+        /**
+         * UBI uses a 128 bit tweak
+         */
+        private long tweak[] = new long[2];
+
+        /**
+         * Whether 64 bit position exceeded
+         */
+        private boolean extendedPosition;
+
+        public UbiTweak()
+        {
+            reset();
+        }
+
+        public void reset(UbiTweak tweak)
+        {
+            this.tweak = Arrays.clone(tweak.tweak, this.tweak);
+            this.extendedPosition = tweak.extendedPosition;
+        }
+
+        public void reset()
+        {
+            tweak[0] = 0;
+            tweak[1] = 0;
+            extendedPosition = false;
+            setFirst(true);
+        }
+
+        public void setType(int type)
+        {
+            // Bits 120..125 = type
+            tweak[1] = (tweak[1] & 0xFFFFFFC000000000L) | ((type & 0x3FL) << 56);
+        }
+
+        public int getType()
+        {
+            return (int)((tweak[1] >>> 56) & 0x3FL);
+        }
+
+        public void setFirst(boolean first)
+        {
+            if (first)
+            {
+                tweak[1] |= T1_FIRST;
+            }
+            else
+            {
+                tweak[1] &= ~T1_FIRST;
+            }
+        }
+
+        public boolean isFirst()
+        {
+            return ((tweak[1] & T1_FIRST) != 0);
+        }
+
+        public void setFinal(boolean last)
+        {
+            if (last)
+            {
+                tweak[1] |= T1_FINAL;
+            }
+            else
+            {
+                tweak[1] &= ~T1_FINAL;
+            }
+        }
+
+        public boolean isFinal()
+        {
+            return ((tweak[1] & T1_FINAL) != 0);
+        }
+
+        /**
+         * Advances the position in the tweak by the specified value.
+         */
+        public void advancePosition(int advance)
+        {
+            // Bits 0..95 = position
+            if (extendedPosition)
+            {
+                long[] parts = new long[3];
+                parts[0] = tweak[0] & 0xFFFFFFFFL;
+                parts[1] = (tweak[0] >>> 32) & 0xFFFFFFFFL;
+                parts[2] = tweak[1] & 0xFFFFFFFFL;
+
+                long carry = advance;
+                for (int i = 0; i < parts.length; i++)
+                {
+                    carry += parts[i];
+                    parts[i] = carry;
+                    carry >>>= 32;
+                }
+                tweak[0] = ((parts[1] & 0xFFFFFFFFL) << 32) | (parts[0] & 0xFFFFFFFFL);
+                tweak[1] = (tweak[1] & 0xFFFFFFFF00000000L) | (parts[2] & 0xFFFFFFFFL);
+            }
+            else
+            {
+                long position = tweak[0];
+                position += advance;
+                tweak[0] = position;
+                if (position > LOW_RANGE)
+                {
+                    extendedPosition = true;
+                }
+            }
+        }
+
+        public long[] getWords()
+        {
+            return tweak;
+        }
+
+        public String toString()
+        {
+            return getType() + " first: " + isFirst() + ", final: " + isFinal();
+        }
+
+    }
+
+    /**
+     * The Unique Block Iteration chaining mode.
+     */
+    // TODO: This might be better as methods...
+    private class UBI
+    {
+        private final UbiTweak tweak = new UbiTweak();
+
+        /**
+         * Buffer for the current block of message data
+         */
+        private byte[] currentBlock;
+
+        /**
+         * Offset into the current message block
+         */
+        private int currentOffset;
+
+        /**
+         * Buffer for message words for feedback into encrypted block
+         */
+        private long[] message;
+
+        public UBI(int blockSize)
+        {
+            currentBlock = new byte[blockSize];
+            message = new long[currentBlock.length / 8];
+        }
+
+        public void reset(UBI ubi)
+        {
+            currentBlock = Arrays.clone(ubi.currentBlock, currentBlock);
+            currentOffset = ubi.currentOffset;
+            message = Arrays.clone(ubi.message, this.message);
+            tweak.reset(ubi.tweak);
+        }
+
+        public void reset(int type)
+        {
+            tweak.reset();
+            tweak.setType(type);
+            currentOffset = 0;
+        }
+
+        public void update(byte[] value, int offset, int len, long[] output)
+        {
+            /*
+             * Buffer complete blocks for the underlying Threefish cipher, only flushing when there
+             * are subsequent bytes (last block must be processed in doFinal() with final=true set).
+             */
+            int copied = 0;
+            while (len > copied)
+            {
+                if (currentOffset == currentBlock.length)
+                {
+                    processBlock(output);
+                    tweak.setFirst(false);
+                    currentOffset = 0;
+                }
+
+                int toCopy = Math.min((len - copied), currentBlock.length - currentOffset);
+                System.arraycopy(value, offset + copied, currentBlock, currentOffset, toCopy);
+                copied += toCopy;
+                currentOffset += toCopy;
+                tweak.advancePosition(toCopy);
+            }
+        }
+
+        private void processBlock(long[] output)
+        {
+            threefish.init(true, chain, tweak.getWords());
+            for (int i = 0; i < message.length; i++)
+            {
+                message[i] = ThreefishEngine.bytesToWord(currentBlock, i * 8);
+            }
+
+            threefish.processBlock(message, output);
+
+            for (int i = 0; i < output.length; i++)
+            {
+                output[i] ^= message[i];
+            }
+        }
+
+        public void doFinal(long[] output)
+        {
+            // Pad remainder of current block with zeroes
+            for (int i = currentOffset; i < currentBlock.length; i++)
+            {
+                currentBlock[i] = 0;
+            }
+
+            tweak.setFinal(true);
+            processBlock(output);
+        }
+
+    }
+
+    /**
+     * Underlying Threefish tweakable block cipher
+     */
+    final ThreefishEngine threefish;
+
+    /**
+     * Size of the digest output, in bytes
+     */
+    private final int outputSizeBytes;
+
+    /**
+     * The current chaining/state value
+     */
+    long[] chain;
+
+    /**
+     * The initial state value
+     */
+    private long[] initialState;
+
+    /**
+     * The (optional) key parameter
+     */
+    private byte[] key;
+
+    /**
+     * Parameters to apply prior to the message
+     */
+    private Parameter[] preMessageParameters;
+
+    /**
+     * Parameters to apply after the message, but prior to output
+     */
+    private Parameter[] postMessageParameters;
+
+    /**
+     * The current UBI operation
+     */
+    private final UBI ubi;
+
+    /**
+     * Buffer for single byte update method
+     */
+    private final byte[] singleByte = new byte[1];
+
+    /**
+     * Constructs a Skein engine.
+     *
+     * @param blockSizeBits  the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+     *                       {@link #SKEIN_1024}.
+     * @param outputSizeBits the output/digest size to produce in bits, which must be an integral number of
+     *                       bytes.
+     */
+    public SkeinEngine(int blockSizeBits, int outputSizeBits)
+    {
+        if (outputSizeBits % 8 != 0)
+        {
+            throw new IllegalArgumentException("Output size must be a multiple of 8 bits. :" + outputSizeBits);
+        }
+        // TODO: Prevent digest sizes > block size?
+        this.outputSizeBytes = outputSizeBits / 8;
+
+        this.threefish = new ThreefishEngine(blockSizeBits);
+        this.ubi = new UBI(threefish.getBlockSize());
+    }
+
+    /**
+     * Creates a SkeinEngine as an exact copy of an existing instance.
+     */
+    public SkeinEngine(SkeinEngine engine)
+    {
+        this(engine.getBlockSize() * 8, engine.getOutputSize() * 8);
+        copyIn(engine);
+    }
+
+    private void copyIn(SkeinEngine engine)
+    {
+        this.ubi.reset(engine.ubi);
+        this.chain = Arrays.clone(engine.chain, this.chain);
+        this.initialState = Arrays.clone(engine.initialState, this.initialState);
+        this.key = Arrays.clone(engine.key, this.key);
+        this.preMessageParameters = clone(engine.preMessageParameters, this.preMessageParameters);
+        this.postMessageParameters = clone(engine.postMessageParameters, this.postMessageParameters);
+    }
+
+    private static Parameter[] clone(Parameter[] data, Parameter[] existing)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+        if ((existing == null) || (existing.length != data.length))
+        {
+            existing = new Parameter[data.length];
+        }
+        System.arraycopy(data, 0, existing, 0, existing.length);
+        return existing;
+    }
+
+    public Memoable copy()
+    {
+        return new SkeinEngine(this);
+    }
+
+    public void reset(Memoable other)
+    {
+        SkeinEngine s = (SkeinEngine)other;
+        if ((getBlockSize() != s.getBlockSize()) || (outputSizeBytes != s.outputSizeBytes))
+        {
+            throw new IllegalArgumentException("Incompatible parameters in provided SkeinEngine.");
+        }
+        copyIn(s);
+    }
+
+    public int getOutputSize()
+    {
+        return outputSizeBytes;
+    }
+
+    public int getBlockSize()
+    {
+        return threefish.getBlockSize();
+    }
+
+    /**
+     * Initialises the Skein engine with the provided parameters. See {@link SkeinParameters} for
+     * details on the parameterisation of the Skein hash function.
+     *
+     * @param params the parameters to apply to this engine, or <code>null</code> to use no parameters.
+     */
+    public void init(SkeinParameters params)
+    {
+        this.chain = null;
+        this.key = null;
+        this.preMessageParameters = null;
+        this.postMessageParameters = null;
+
+        if (params != null)
+        {
+            byte[] key = params.getKey();
+            if (key.length < 16)
+            {
+                throw new IllegalArgumentException("Skein key must be at least 128 bits.");
+            }
+            initParams(params.getParameters());
+        }
+        createInitialState();
+
+        // Initialise message block
+        ubiInit(PARAM_TYPE_MESSAGE);
+    }
+
+    private void initParams(Hashtable parameters)
+    {
+        Enumeration keys = parameters.keys();
+        final Vector pre = new Vector();
+        final Vector post = new Vector();
+
+        while (keys.hasMoreElements())
+        {
+            Integer type = (Integer)keys.nextElement();
+            byte[] value = (byte[])parameters.get(type);
+
+            if (type.intValue() == PARAM_TYPE_KEY)
+            {
+                this.key = value;
+            }
+            else if (type.intValue() < PARAM_TYPE_MESSAGE)
+            {
+                pre.addElement(new Parameter(type.intValue(), value));
+            }
+            else
+            {
+                post.addElement(new Parameter(type.intValue(), value));
+            }
+        }
+        preMessageParameters = new Parameter[pre.size()];
+        pre.copyInto(preMessageParameters);
+        sort(preMessageParameters);
+
+        postMessageParameters = new Parameter[post.size()];
+        post.copyInto(postMessageParameters);
+        sort(postMessageParameters);
+    }
+
+    private static void sort(Parameter[] params)
+    {
+        if (params == null)
+        {
+            return;
+        }
+        // Insertion sort, for Java 1.1 compatibility
+        for (int i = 1; i < params.length; i++)
+        {
+            Parameter param = params[i];
+            int hole = i;
+            while (hole > 0 && param.getType() < params[hole - 1].getType())
+            {
+                params[hole] = params[hole - 1];
+                hole = hole - 1;
+            }
+            params[hole] = param;
+        }
+    }
+
+    /**
+     * Calculate the initial (pre message block) chaining state.
+     */
+    private void createInitialState()
+    {
+        long[] precalc = (long[])INITIAL_STATES.get(variantIdentifier(getBlockSize(), getOutputSize()));
+        if ((key == null) && (precalc != null))
+        {
+            // Precalculated UBI(CFG)
+            chain = Arrays.clone(precalc);
+        }
+        else
+        {
+            // Blank initial state
+            chain = new long[getBlockSize() / 8];
+
+            // Process key block
+            if (key != null)
+            {
+                ubiComplete(SkeinParameters.PARAM_TYPE_KEY, key);
+            }
+
+            // Process configuration block
+            ubiComplete(PARAM_TYPE_CONFIG, new Configuration(outputSizeBytes * 8).getBytes());
+        }
+
+        // Process additional pre-message parameters
+        if (preMessageParameters != null)
+        {
+            for (int i = 0; i < preMessageParameters.length; i++)
+            {
+                Parameter param = preMessageParameters[i];
+                ubiComplete(param.getType(), param.getValue());
+            }
+        }
+        initialState = Arrays.clone(chain);
+    }
+
+    /**
+     * Reset the engine to the initial state (with the key and any pre-message parameters , ready to
+     * accept message input.
+     */
+    public void reset()
+    {
+        System.arraycopy(initialState, 0, chain, 0, chain.length);
+
+        ubiInit(PARAM_TYPE_MESSAGE);
+    }
+
+    private void ubiComplete(int type, byte[] value)
+    {
+        ubiInit(type);
+        this.ubi.update(value, 0, value.length, chain);
+        ubiFinal();
+    }
+
+    private void ubiInit(int type)
+    {
+        this.ubi.reset(type);
+    }
+
+    private void ubiFinal()
+    {
+        ubi.doFinal(chain);
+    }
+
+    private void checkInitialised()
+    {
+        if (this.ubi == null)
+        {
+            throw new IllegalArgumentException("Skein engine is not initialised.");
+        }
+    }
+
+    public void update(byte in)
+    {
+        singleByte[0] = in;
+        update(singleByte, 0, 1);
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        checkInitialised();
+        ubi.update(in, inOff, len, chain);
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        checkInitialised();
+        if (out.length < (outOff + outputSizeBytes))
+        {
+            throw new DataLengthException("Output buffer is too short to hold output of " + outputSizeBytes + " bytes");
+        }
+
+        // Finalise message block
+        ubiFinal();
+
+        // Process additional post-message parameters
+        if (postMessageParameters != null)
+        {
+            for (int i = 0; i < postMessageParameters.length; i++)
+            {
+                Parameter param = postMessageParameters[i];
+                ubiComplete(param.getType(), param.getValue());
+            }
+        }
+
+        // Perform the output transform
+        final int blockSize = getBlockSize();
+        final int blocksRequired = ((outputSizeBytes + blockSize - 1) / blockSize);
+        for (int i = 0; i < blocksRequired; i++)
+        {
+            final int toWrite = Math.min(blockSize, outputSizeBytes - (i * blockSize));
+            output(i, out, outOff + (i * blockSize), toWrite);
+        }
+
+        reset();
+
+        return outputSizeBytes;
+    }
+
+    private void output(long outputSequence, byte[] out, int outOff, int outputBytes)
+    {
+        byte[] currentBytes = new byte[8];
+        ThreefishEngine.wordToBytes(outputSequence, currentBytes, 0);
+
+        // Output is a sequence of UBI invocations all of which use and preserve the pre-output
+        // state
+        long[] outputWords = new long[chain.length];
+        ubiInit(PARAM_TYPE_OUTPUT);
+        this.ubi.update(currentBytes, 0, currentBytes.length, outputWords);
+        ubi.doFinal(outputWords);
+
+        final int wordsRequired = ((outputBytes + 8 - 1) / 8);
+        for (int i = 0; i < wordsRequired; i++)
+        {
+            int toWrite = Math.min(8, outputBytes - (i * 8));
+            if (toWrite == 8)
+            {
+                ThreefishEngine.wordToBytes(outputWords[i], out, outOff + (i * 8));
+            }
+            else
+            {
+                ThreefishEngine.wordToBytes(outputWords[i], currentBytes, 0);
+                System.arraycopy(currentBytes, 0, out, outOff + (i * 8), toWrite);
+            }
+        }
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html
deleted file mode 100644
index 0a0d95c..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/digests/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Message digest classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
index c8c548e..19c0beb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalDecryptor.java
@@ -43,6 +43,6 @@
 
         ECPoint tmp = pair.getX().multiply(key.getD());
 
-        return pair.getY().add(tmp.negate());
+        return pair.getY().add(tmp.negate()).normalize();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
index e5569a8..2a0b78d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECElGamalEncryptor.java
@@ -6,7 +6,6 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
-import org.bouncycastle.math.ec.ECConstants;
 import org.bouncycastle.math.ec.ECPoint;
 
 /**
@@ -69,6 +68,6 @@
         ECPoint  gamma = g.multiply(k);
         ECPoint  phi = key.getQ().multiply(k).add(point);
 
-        return new ECPair(gamma, phi);
+        return new ECPair(gamma.normalize(), phi.normalize());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
new file mode 100644
index 0000000..e35e077
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECFixedTransform.java
@@ -0,0 +1,71 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * this transforms the original randomness used for an ElGamal encryption by a fixed value.
+ */
+public class ECFixedTransform
+    implements ECPairFactorTransform
+{
+    private ECPublicKeyParameters key;
+
+    private BigInteger k;
+
+    public ECFixedTransform(BigInteger k)
+    {
+        this.k = k;
+    }
+
+    /**
+     * initialise the underlying EC ElGamal engine.
+     *
+     * @param param the necessary EC key parameters.
+     */
+    public void init(
+        CipherParameters    param)
+    {
+        if (!(param instanceof ECPublicKeyParameters))
+        {
+            throw new IllegalArgumentException("ECPublicKeyParameters are required for fixed transform.");
+        }
+
+        this.key = (ECPublicKeyParameters)param;
+    }
+
+    /**
+     * Transform an existing cipher test pair using the ElGamal algorithm. Note: it is assumed this
+     * transform has been initialised with the same public key that was used to create the original
+     * cipher text.
+     *
+     * @param cipherText the EC point to process.
+     * @return returns a new ECPair representing the result of the process.
+     */
+    public ECPair transform(ECPair cipherText)
+    {
+        if (key == null)
+        {
+            throw new IllegalStateException("ECFixedTransform not initialised");
+        }
+
+        ECPoint  g = key.getParameters().getG();
+        ECPoint  gamma = g.multiply(k);
+        ECPoint  phi = key.getQ().multiply(k).add(cipherText.getY());
+
+        return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+    }
+
+    /**
+     * Return the last transform value used by the transform
+     *
+     * @return a BigInteger representing k value.
+     */
+    public BigInteger getTransformValue()
+    {
+        return k;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
index 32ba070..74016c1 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewPublicKeyTransform.java
@@ -69,6 +69,6 @@
         ECPoint  gamma = g.multiply(k);
         ECPoint  phi = key.getQ().multiply(k).add(cipherText.getY());
 
-        return new ECPair(gamma, phi);
+        return new ECPair(gamma.normalize(), phi.normalize());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
index b037984..b293759 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECNewRandomnessTransform.java
@@ -12,11 +12,13 @@
  * this transforms the original randomness used for an ElGamal encryption.
  */
 public class ECNewRandomnessTransform
-    implements ECPairTransform
+    implements ECPairFactorTransform
 {
     private ECPublicKeyParameters key;
     private SecureRandom          random;
 
+    private BigInteger            lastK;
+
     /**
      * initialise the underlying EC ElGamal engine.
      *
@@ -71,6 +73,18 @@
         ECPoint  gamma = g.multiply(k);
         ECPoint  phi = key.getQ().multiply(k).add(cipherText.getY());
 
-        return new ECPair(cipherText.getX().add(gamma), phi);
+        lastK = k;
+
+        return new ECPair(cipherText.getX().add(gamma).normalize(), phi.normalize());
+    }
+
+    /**
+     * Return the last random value generated for a transform
+     *
+     * @return a BigInteger representing the last random value.
+     */
+    public BigInteger getTransformValue()
+    {
+        return lastK;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
index d910f3c..ea3b4b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPair.java
@@ -23,16 +23,18 @@
         return y;
     }
 
-    public byte[] getEncoded()
+    public boolean equals(ECPair other)
     {
-        byte[] xEnc = x.getEncoded();
-        byte[] yEnc = y.getEncoded();
+        return other.getX().equals(getX()) && other.getY().equals(getY());
+    }
 
-        byte[] full = new byte[xEnc.length + yEnc.length];
+    public boolean equals(Object other)
+    {
+        return other instanceof ECPair ? equals((ECPair)other) : false;
+    }
 
-        System.arraycopy(xEnc, 0, full, 0, xEnc.length);
-        System.arraycopy(yEnc, 0, full, xEnc.length, yEnc.length);
-
-        return full;
+    public int hashCode()
+    {
+        return x.hashCode() + 37 * y.hashCode();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java
new file mode 100644
index 0000000..be48551
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/ec/ECPairFactorTransform.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.crypto.ec;
+
+import java.math.BigInteger;
+
+public interface ECPairFactorTransform
+    extends ECPairTransform
+{
+    /**
+     * Return the last value used to calculated a transform.
+     *
+     * @return a BigInteger representing the last transform value used.
+     */
+    BigInteger getTransformValue();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html
deleted file mode 100644
index d50edcf..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/ec/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Lightweight EC point operations, such as EC ElGamal and randomness transforms.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html
deleted file mode 100644
index fc56f63..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/encodings/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Block encodings for asymmetric ciphers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
new file mode 100644
index 0000000..2d1de39
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ChaChaEngine.java
@@ -0,0 +1,186 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Implementation of Daniel J. Bernstein's ChaCha stream cipher.
+ */
+public class ChaChaEngine extends Salsa20Engine
+{
+
+    /**
+     * Creates a 20 rounds ChaCha engine.
+     */
+    public ChaChaEngine()
+    {
+        super();
+    }
+
+    /**
+     * Creates a ChaCha engine with a specific number of rounds.
+     * @param rounds the number of rounds (must be an even number).
+     */
+    public ChaChaEngine(int rounds)
+    {
+        super(rounds);
+    }
+
+    public String getAlgorithmName()
+    {
+        return "ChaCha" + rounds;
+    }
+
+    protected void advanceCounter()
+    {
+        if (++engineState[12] == 0)
+        {
+            ++engineState[13];
+        }
+    }
+
+    protected void resetCounter()
+    {
+        engineState[12] = engineState[13] = 0;
+    }
+
+    protected void setKey(byte[] keyBytes, byte[] ivBytes)
+    {
+        if ((keyBytes.length != 16) && (keyBytes.length != 32))
+        {
+            throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+        }
+
+        int offset = 0;
+        byte[] constants;
+
+        // Key
+        engineState[4] = Pack.littleEndianToInt(keyBytes, 0);
+        engineState[5] = Pack.littleEndianToInt(keyBytes, 4);
+        engineState[6] = Pack.littleEndianToInt(keyBytes, 8);
+        engineState[7] = Pack.littleEndianToInt(keyBytes, 12);
+
+        if (keyBytes.length == 32)
+        {
+            constants = sigma;
+            offset = 16;
+        } else
+        {
+            constants = tau;
+        }
+
+        engineState[8] = Pack.littleEndianToInt(keyBytes, offset);
+        engineState[9] = Pack.littleEndianToInt(keyBytes, offset + 4);
+        engineState[10] = Pack.littleEndianToInt(keyBytes, offset + 8);
+        engineState[11] = Pack.littleEndianToInt(keyBytes, offset + 12);
+
+        engineState[0] = Pack.littleEndianToInt(constants, 0);
+        engineState[1] = Pack.littleEndianToInt(constants, 4);
+        engineState[2] = Pack.littleEndianToInt(constants, 8);
+        engineState[3] = Pack.littleEndianToInt(constants, 12);
+
+        // Counter
+        engineState[12] = engineState[13] = 0;
+
+        // IV
+        engineState[14] = Pack.littleEndianToInt(ivBytes, 0);
+        engineState[15] = Pack.littleEndianToInt(ivBytes, 4);
+    }
+
+    protected void generateKeyStream(byte[] output)
+    {
+        chachaCore(rounds, engineState, x);
+        Pack.intToLittleEndian(x, output, 0);
+    }
+
+    /**
+     * ChacCha function
+     *
+     * @param   input   input data
+     *
+     * @return  keystream
+     */    
+    public static void chachaCore(int rounds, int[] input, int[] x)
+    {
+        if (input.length != 16) {
+            throw new IllegalArgumentException();
+        }
+        if (x.length != 16) {
+            throw new IllegalArgumentException();
+        }
+        if (rounds % 2 != 0) {
+            throw new IllegalArgumentException("Number of rounds must be even");
+        }
+
+        int x00 = input[ 0];
+        int x01 = input[ 1];
+        int x02 = input[ 2];
+        int x03 = input[ 3];
+        int x04 = input[ 4];
+        int x05 = input[ 5];
+        int x06 = input[ 6];
+        int x07 = input[ 7];
+        int x08 = input[ 8];
+        int x09 = input[ 9];
+        int x10 = input[10];
+        int x11 = input[11];
+        int x12 = input[12];
+        int x13 = input[13];
+        int x14 = input[14];
+        int x15 = input[15];
+
+        for (int i = rounds; i > 0; i -= 2)
+        {
+            x00 += x04; x12 = rotl(x12 ^ x00, 16);
+            x08 += x12; x04 = rotl(x04 ^ x08, 12);
+            x00 += x04; x12 = rotl(x12 ^ x00, 8);
+            x08 += x12; x04 = rotl(x04 ^ x08, 7);
+            x01 += x05; x13 = rotl(x13 ^ x01, 16);
+            x09 += x13; x05 = rotl(x05 ^ x09, 12);
+            x01 += x05; x13 = rotl(x13 ^ x01, 8);
+            x09 += x13; x05 = rotl(x05 ^ x09, 7);
+            x02 += x06; x14 = rotl(x14 ^ x02, 16);
+            x10 += x14; x06 = rotl(x06 ^ x10, 12);
+            x02 += x06; x14 = rotl(x14 ^ x02, 8);
+            x10 += x14; x06 = rotl(x06 ^ x10, 7);
+            x03 += x07; x15 = rotl(x15 ^ x03, 16);
+            x11 += x15; x07 = rotl(x07 ^ x11, 12);
+            x03 += x07; x15 = rotl(x15 ^ x03, 8);
+            x11 += x15; x07 = rotl(x07 ^ x11, 7);
+            x00 += x05; x15 = rotl(x15 ^ x00, 16);
+            x10 += x15; x05 = rotl(x05 ^ x10, 12);
+            x00 += x05; x15 = rotl(x15 ^ x00, 8);
+            x10 += x15; x05 = rotl(x05 ^ x10, 7);
+            x01 += x06; x12 = rotl(x12 ^ x01, 16);
+            x11 += x12; x06 = rotl(x06 ^ x11, 12);
+            x01 += x06; x12 = rotl(x12 ^ x01, 8);
+            x11 += x12; x06 = rotl(x06 ^ x11, 7);
+            x02 += x07; x13 = rotl(x13 ^ x02, 16);
+            x08 += x13; x07 = rotl(x07 ^ x08, 12);
+            x02 += x07; x13 = rotl(x13 ^ x02, 8);
+            x08 += x13; x07 = rotl(x07 ^ x08, 7);
+            x03 += x04; x14 = rotl(x14 ^ x03, 16);
+            x09 += x14; x04 = rotl(x04 ^ x09, 12);
+            x03 += x04; x14 = rotl(x14 ^ x03, 8);
+            x09 += x14; x04 = rotl(x04 ^ x09, 7);
+
+        }
+
+        x[ 0] = x00 + input[ 0];
+        x[ 1] = x01 + input[ 1];
+        x[ 2] = x02 + input[ 2];
+        x[ 3] = x03 + input[ 3];
+        x[ 4] = x04 + input[ 4];
+        x[ 5] = x05 + input[ 5];
+        x[ 6] = x06 + input[ 6];
+        x[ 7] = x07 + input[ 7];
+        x[ 8] = x08 + input[ 8];
+        x[ 9] = x09 + input[ 9];
+        x[10] = x10 + input[10];
+        x[11] = x11 + input[11];
+        x[12] = x12 + input[12];
+        x[13] = x13 + input[13];
+        x[14] = x14 + input[14];
+        x[15] = x15 + input[15];
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
index 6b3da1c..89271f0 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grain128Engine.java
@@ -89,8 +89,7 @@
         System.arraycopy(iv, 0, workingIV, 0, iv.length);
         System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
 
-        setKey(workingKey, workingIV);
-        initGrain();
+        reset();
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
index c3baaec..782a93c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Grainv1Engine.java
@@ -89,8 +89,7 @@
         System.arraycopy(iv, 0, workingIV, 0, iv.length);
         System.arraycopy(key.getKey(), 0, workingKey, 0, key.getKey().length);
 
-        setKey(workingKey, workingIV);
-        initGrain();
+        reset();
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
index 69da0f0..015e49e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC128Engine.java
@@ -118,6 +118,7 @@
                 "The key must be 128 bits long");
         }
 
+        idx = 0;
         cnt = 0;
 
         int[] w = new int[1280];
@@ -246,7 +247,6 @@
 
     public void reset()
     {
-        idx = 0;
         init();
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
index 538d244..8bd7e9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/HC256Engine.java
@@ -99,6 +99,7 @@
             iv = newIV;
         }
 
+        idx = 0;
         cnt = 0;
 
         int[] w = new int[2560];
@@ -226,7 +227,6 @@
 
     public void reset()
     {
-        idx = 0;
         init();
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
index 95a395a..1ad2c9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/NullEngine.java
@@ -12,14 +12,25 @@
 public class NullEngine implements BlockCipher
 {
     private boolean initialised;
-    protected static final int BLOCK_SIZE = 1;
-    
+    protected static final int DEFAULT_BLOCK_SIZE = 1;
+    private final int blockSize;
+
     /**
-     * Standard constructor.
+     * Constructs a null engine with a block size of 1 byte.
      */
     public NullEngine()
     {
-        super();
+        this(DEFAULT_BLOCK_SIZE);
+    }
+
+    /**
+     * Constructs a null engine with a specific block size.
+     * 
+     * @param blockSize the block size in bytes.
+     */
+    public NullEngine(int blockSize)
+    {
+        this.blockSize = blockSize;
     }
 
     /* (non-Javadoc)
@@ -44,7 +55,7 @@
      */
     public int getBlockSize()
     {
-        return BLOCK_SIZE;
+        return blockSize;
     }
 
     /* (non-Javadoc)
@@ -57,22 +68,22 @@
         {
             throw new IllegalStateException("Null engine not initialised");
         }
-            if ((inOff + BLOCK_SIZE) > in.length)
-            {
-                throw new DataLengthException("input buffer too short");
-            }
+        if ((inOff + blockSize) > in.length)
+        {
+            throw new DataLengthException("input buffer too short");
+        }
 
-            if ((outOff + BLOCK_SIZE) > out.length)
-            {
-                throw new OutputLengthException("output buffer too short");
-            }
-            
-            for (int i = 0; i < BLOCK_SIZE; ++i)
-            {
-                out[outOff + i] = in[inOff + i];
-            }
-            
-            return BLOCK_SIZE;
+        if ((outOff + blockSize) > out.length)
+        {
+            throw new OutputLengthException("output buffer too short");
+        }
+
+        for (int i = 0; i < blockSize; ++i)
+        {
+            out[outOff + i] = in[inOff + i];
+        }
+
+        return blockSize;
     }
 
     /* (non-Javadoc)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
index 540bd25..cfd86fb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RFC3394WrapEngine.java
@@ -85,7 +85,7 @@
         byte[]  buf = new byte[8 + iv.length];
 
         System.arraycopy(iv, 0, block, 0, iv.length);
-        System.arraycopy(in, 0, block, iv.length, inLen);
+        System.arraycopy(in, inOff, block, iv.length, inLen);
 
         engine.init(true, param);
 
@@ -137,8 +137,8 @@
         byte[]  a = new byte[iv.length];
         byte[]  buf = new byte[8 + iv.length];
 
-        System.arraycopy(in, 0, a, 0, iv.length);
-        System.arraycopy(in, iv.length, block, 0, inLen - iv.length);
+        System.arraycopy(in, inOff, a, 0, iv.length);
+        System.arraycopy(in, inOff + iv.length, block, 0, inLen - iv.length);
 
         engine.init(false, param);
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
index e7fb943..c9765bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/RSABlindedEngine.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.crypto.engines;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 import org.bouncycastle.crypto.AsymmetricBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
@@ -8,16 +11,13 @@
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 import org.bouncycastle.util.BigIntegers;
 
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
 /**
  * this does your basic RSA algorithm with blinding
  */
 public class RSABlindedEngine
     implements AsymmetricBlockCipher
 {
-    private static BigInteger ONE = BigInteger.valueOf(1);
+    private static final BigInteger ONE = BigInteger.valueOf(1);
 
     private RSACoreEngine    core = new RSACoreEngine();
     private RSAKeyParameters key;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
index 6d4210d..2d6140d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Salsa20Engine.java
@@ -13,27 +13,28 @@
 /**
  * Implementation of Daniel J. Bernstein's Salsa20 stream cipher, Snuffle 2005
  */
-
 public class Salsa20Engine
     implements StreamCipher
 {
+    public final static int DEFAULT_ROUNDS = 20;
+
     /** Constants */
     private final static int STATE_SIZE = 16; // 16, 32 bit ints = 64 bytes
 
-    private final static byte[]
+    protected final static byte[]
         sigma = Strings.toByteArray("expand 32-byte k"),
         tau   = Strings.toByteArray("expand 16-byte k");
 
+    protected int rounds;
+
     /*
      * variables to hold the state of the engine
      * during encryption and decryption
      */
     private int         index = 0;
-    private int[]       engineState = new int[STATE_SIZE]; // state
-    private int[]       x = new int[STATE_SIZE] ; // internal buffer
-    private byte[]      keyStream   = new byte[STATE_SIZE * 4], // expanded state, 64 bytes
-                        workingKey  = null,
-                        workingIV   = null;
+    protected int[]     engineState = new int[STATE_SIZE]; // state
+    protected int[]       x = new int[STATE_SIZE] ; // internal buffer
+    private byte[]      keyStream   = new byte[STATE_SIZE * 4]; // expanded state, 64 bytes
     private boolean     initialised = false;
 
     /*
@@ -42,6 +43,28 @@
     private int cW0, cW1, cW2;
 
     /**
+     * Creates a 20 round Salsa20 engine.
+     */
+    public Salsa20Engine()
+    {
+        this(DEFAULT_ROUNDS);
+    }
+
+    /**
+     * Creates a Salsa20 engine with a specific number of rounds.
+     * @param rounds the number of rounds (must be an even number).
+     */
+    public Salsa20Engine(int rounds)
+    {
+        if (rounds <= 0 || (rounds & 1) != 0)
+        {
+            throw new IllegalArgumentException("'rounds' must be a positive, even number");
+        }
+
+        this.rounds = rounds;
+    }
+
+    /**
      * initialise a Salsa20 cipher.
      *
      * @param forEncryption whether or not we are for encryption.
@@ -61,34 +84,43 @@
 
         if (!(params instanceof ParametersWithIV))
         {
-            throw new IllegalArgumentException("Salsa20 Init parameters must include an IV");
+            throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include an IV");
         }
 
         ParametersWithIV ivParams = (ParametersWithIV) params;
 
         byte[] iv = ivParams.getIV();
-
-        if (iv == null || iv.length != 8)
+        if (iv == null || iv.length != getNonceSize())
         {
-            throw new IllegalArgumentException("Salsa20 requires exactly 8 bytes of IV");
+            throw new IllegalArgumentException(getAlgorithmName() + " requires exactly " + getNonceSize()
+                    + " bytes of IV");
         }
 
         if (!(ivParams.getParameters() instanceof KeyParameter))
         {
-            throw new IllegalArgumentException("Salsa20 Init parameters must include a key");
+            throw new IllegalArgumentException(getAlgorithmName() + " Init parameters must include a key");
         }
 
         KeyParameter key = (KeyParameter) ivParams.getParameters();
 
-        workingKey = key.getKey();
-        workingIV = iv;
+        setKey(key.getKey(), iv);
+        reset();
+        initialised = true;
+    }
 
-        setKey(workingKey, workingIV);
+    protected int getNonceSize()
+    {
+        return 8;
     }
 
     public String getAlgorithmName()
     {
-        return "Salsa20";
+        String name = "Salsa20";
+        if (rounds != DEFAULT_ROUNDS)
+        {
+            name += "/" + rounds;
+        }
+        return name;
     }
 
     public byte returnByte(byte in)
@@ -101,11 +133,7 @@
         if (index == 0)
         {
             generateKeyStream(keyStream);
-
-            if (++engineState[8] == 0)
-            {
-                ++engineState[9];
-            }
+            advanceCounter();
         }
 
         byte out = (byte)(keyStream[index]^in);
@@ -114,6 +142,14 @@
         return out;
     }
 
+    protected void advanceCounter()
+    {
+        if (++engineState[8] == 0)
+        {
+            ++engineState[9];
+        }
+    }
+
     public void processBytes(
         byte[]     in, 
         int     inOff, 
@@ -123,7 +159,7 @@
     {
         if (!initialised)
         {
-            throw new IllegalStateException(getAlgorithmName()+" not initialised");
+            throw new IllegalStateException(getAlgorithmName() + " not initialised");
         }
 
         if ((inOff + len) > in.length)
@@ -146,11 +182,7 @@
             if (index == 0)
             {
                 generateKeyStream(keyStream);
-
-                if (++engineState[8] == 0)
-                {
-                    ++engineState[9];
-                }
+                advanceCounter();
             }
 
             out[i+outOff] = (byte)(keyStream[index]^in[i+inOff]);
@@ -160,28 +192,32 @@
 
     public void reset()
     {
-        setKey(workingKey, workingIV);
+        index = 0;
+        resetLimitCounter();
+        resetCounter();
     }
 
-    // Private implementation
-
-    private void setKey(byte[] keyBytes, byte[] ivBytes)
+    protected void resetCounter()
     {
-        workingKey = keyBytes;
-        workingIV  = ivBytes;
+        engineState[8] = engineState[9] = 0;
+    }
 
-        index = 0;
-        resetCounter();
+    protected void setKey(byte[] keyBytes, byte[] ivBytes)
+    {
+        if ((keyBytes.length != 16) && (keyBytes.length != 32)) {
+            throw new IllegalArgumentException(getAlgorithmName() + " requires 128 bit or 256 bit key");
+        }
+        
         int offset = 0;
         byte[] constants;
 
         // Key
-        engineState[1] = Pack.littleEndianToInt(workingKey, 0);
-        engineState[2] = Pack.littleEndianToInt(workingKey, 4);
-        engineState[3] = Pack.littleEndianToInt(workingKey, 8);
-        engineState[4] = Pack.littleEndianToInt(workingKey, 12);
+        engineState[1] = Pack.littleEndianToInt(keyBytes, 0);
+        engineState[2] = Pack.littleEndianToInt(keyBytes, 4);
+        engineState[3] = Pack.littleEndianToInt(keyBytes, 8);
+        engineState[4] = Pack.littleEndianToInt(keyBytes, 12);
 
-        if (workingKey.length == 32)
+        if (keyBytes.length == 32)
         {
             constants = sigma;
             offset = 16;
@@ -191,26 +227,25 @@
             constants = tau;
         }
 
-        engineState[11] = Pack.littleEndianToInt(workingKey, offset);
-        engineState[12] = Pack.littleEndianToInt(workingKey, offset+4);
-        engineState[13] = Pack.littleEndianToInt(workingKey, offset+8);
-        engineState[14] = Pack.littleEndianToInt(workingKey, offset+12);
+        engineState[11] = Pack.littleEndianToInt(keyBytes, offset);
+        engineState[12] = Pack.littleEndianToInt(keyBytes, offset+4);
+        engineState[13] = Pack.littleEndianToInt(keyBytes, offset+8);
+        engineState[14] = Pack.littleEndianToInt(keyBytes, offset+12);
+
         engineState[0 ] = Pack.littleEndianToInt(constants, 0);
         engineState[5 ] = Pack.littleEndianToInt(constants, 4);
         engineState[10] = Pack.littleEndianToInt(constants, 8);
         engineState[15] = Pack.littleEndianToInt(constants, 12);
 
         // IV
-        engineState[6] = Pack.littleEndianToInt(workingIV, 0);
-        engineState[7] = Pack.littleEndianToInt(workingIV, 4);
-        engineState[8] = engineState[9] = 0;
-
-        initialised = true;
+        engineState[6] = Pack.littleEndianToInt(ivBytes, 0);
+        engineState[7] = Pack.littleEndianToInt(ivBytes, 4);
+        resetCounter();
     }
 
-    private void generateKeyStream(byte[] output)
+    protected void generateKeyStream(byte[] output)
     {
-        salsaCore(20, engineState, x);
+        salsaCore(rounds, engineState, x);
         Pack.intToLittleEndian(x, output, 0);
     }
 
@@ -223,50 +258,86 @@
      */    
     public static void salsaCore(int rounds, int[] input, int[] x)
     {
-        // TODO Exception if rounds odd?
+        if (input.length != 16) {
+            throw new IllegalArgumentException();
+        }
+        if (x.length != 16) {
+            throw new IllegalArgumentException();
+        }
+        if (rounds % 2 != 0) {
+            throw new IllegalArgumentException("Number of rounds must be even");
+        }
 
-        System.arraycopy(input, 0, x, 0, input.length);
+        int x00 = input[ 0];
+        int x01 = input[ 1];
+        int x02 = input[ 2];
+        int x03 = input[ 3];
+        int x04 = input[ 4];
+        int x05 = input[ 5];
+        int x06 = input[ 6];
+        int x07 = input[ 7];
+        int x08 = input[ 8];
+        int x09 = input[ 9];
+        int x10 = input[10];
+        int x11 = input[11];
+        int x12 = input[12];
+        int x13 = input[13];
+        int x14 = input[14];
+        int x15 = input[15];
 
         for (int i = rounds; i > 0; i -= 2)
         {
-            x[ 4] ^= rotl((x[ 0]+x[12]), 7);
-            x[ 8] ^= rotl((x[ 4]+x[ 0]), 9);
-            x[12] ^= rotl((x[ 8]+x[ 4]),13);
-            x[ 0] ^= rotl((x[12]+x[ 8]),18);
-            x[ 9] ^= rotl((x[ 5]+x[ 1]), 7);
-            x[13] ^= rotl((x[ 9]+x[ 5]), 9);
-            x[ 1] ^= rotl((x[13]+x[ 9]),13);
-            x[ 5] ^= rotl((x[ 1]+x[13]),18);
-            x[14] ^= rotl((x[10]+x[ 6]), 7);
-            x[ 2] ^= rotl((x[14]+x[10]), 9);
-            x[ 6] ^= rotl((x[ 2]+x[14]),13);
-            x[10] ^= rotl((x[ 6]+x[ 2]),18);
-            x[ 3] ^= rotl((x[15]+x[11]), 7);
-            x[ 7] ^= rotl((x[ 3]+x[15]), 9);
-            x[11] ^= rotl((x[ 7]+x[ 3]),13);
-            x[15] ^= rotl((x[11]+x[ 7]),18);
-            x[ 1] ^= rotl((x[ 0]+x[ 3]), 7);
-            x[ 2] ^= rotl((x[ 1]+x[ 0]), 9);
-            x[ 3] ^= rotl((x[ 2]+x[ 1]),13);
-            x[ 0] ^= rotl((x[ 3]+x[ 2]),18);
-            x[ 6] ^= rotl((x[ 5]+x[ 4]), 7);
-            x[ 7] ^= rotl((x[ 6]+x[ 5]), 9);
-            x[ 4] ^= rotl((x[ 7]+x[ 6]),13);
-            x[ 5] ^= rotl((x[ 4]+x[ 7]),18);
-            x[11] ^= rotl((x[10]+x[ 9]), 7);
-            x[ 8] ^= rotl((x[11]+x[10]), 9);
-            x[ 9] ^= rotl((x[ 8]+x[11]),13);
-            x[10] ^= rotl((x[ 9]+x[ 8]),18);
-            x[12] ^= rotl((x[15]+x[14]), 7);
-            x[13] ^= rotl((x[12]+x[15]), 9);
-            x[14] ^= rotl((x[13]+x[12]),13);
-            x[15] ^= rotl((x[14]+x[13]),18);
+            x04 ^= rotl((x00+x12), 7);
+            x08 ^= rotl((x04+x00), 9);
+            x12 ^= rotl((x08+x04),13);
+            x00 ^= rotl((x12+x08),18);
+            x09 ^= rotl((x05+x01), 7);
+            x13 ^= rotl((x09+x05), 9);
+            x01 ^= rotl((x13+x09),13);
+            x05 ^= rotl((x01+x13),18);
+            x14 ^= rotl((x10+x06), 7);
+            x02 ^= rotl((x14+x10), 9);
+            x06 ^= rotl((x02+x14),13);
+            x10 ^= rotl((x06+x02),18);
+            x03 ^= rotl((x15+x11), 7);
+            x07 ^= rotl((x03+x15), 9);
+            x11 ^= rotl((x07+x03),13);
+            x15 ^= rotl((x11+x07),18);
+
+            x01 ^= rotl((x00+x03), 7);
+            x02 ^= rotl((x01+x00), 9);
+            x03 ^= rotl((x02+x01),13);
+            x00 ^= rotl((x03+x02),18);
+            x06 ^= rotl((x05+x04), 7);
+            x07 ^= rotl((x06+x05), 9);
+            x04 ^= rotl((x07+x06),13);
+            x05 ^= rotl((x04+x07),18);
+            x11 ^= rotl((x10+x09), 7);
+            x08 ^= rotl((x11+x10), 9);
+            x09 ^= rotl((x08+x11),13);
+            x10 ^= rotl((x09+x08),18);
+            x12 ^= rotl((x15+x14), 7);
+            x13 ^= rotl((x12+x15), 9);
+            x14 ^= rotl((x13+x12),13);
+            x15 ^= rotl((x14+x13),18);
         }
 
-        for (int i = 0; i < STATE_SIZE; ++i)
-        {
-            x[i] += input[i];
-        }
+        x[ 0] = x00 + input[ 0];
+        x[ 1] = x01 + input[ 1];
+        x[ 2] = x02 + input[ 2];
+        x[ 3] = x03 + input[ 3];
+        x[ 4] = x04 + input[ 4];
+        x[ 5] = x05 + input[ 5];
+        x[ 6] = x06 + input[ 6];
+        x[ 7] = x07 + input[ 7];
+        x[ 8] = x08 + input[ 8];
+        x[ 9] = x09 + input[ 9];
+        x[10] = x10 + input[10];
+        x[11] = x11 + input[11];
+        x[12] = x12 + input[12];
+        x[13] = x13 + input[13];
+        x[14] = x14 + input[14];
+        x[15] = x15 + input[15];
     }
 
     /**
@@ -277,12 +348,12 @@
      *
      * @return  rotated x
      */
-    private static int rotl(int x, int y)
+    protected static int rotl(int x, int y)
     {
         return (x << y) | (x >>> -y);
     }
 
-    private void resetCounter()
+    private void resetLimitCounter()
     {
         cW0 = 0;
         cW1 = 0;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
new file mode 100644
index 0000000..b59205d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/Shacal2Engine.java
@@ -0,0 +1,201 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.OutputLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * Block cipher Shacal2, designed by Helena Handschuh and David Naccache,
+ * based on hash function SHA-256,
+ * using SHA-256-Initialization-Values as data and SHA-256-Data as key.
+ * <p>
+ * A description of Shacal can be found at:
+ *    http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.3.4066
+ * Best known cryptanalytic (Wikipedia 11.2013):
+ *    Related-key rectangle attack on 44-rounds (Jiqiang Lu, Jongsung Kim).
+ * Comments are related to SHA-256-Naming as described in FIPS PUB 180-2
+ * </p>
+ */
+public class Shacal2Engine 
+	implements BlockCipher 
+{
+	private final static int[] K = { // SHA-256-Constants
+			0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 
+			0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 
+			0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, 
+			0x983e5152,	0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, 
+			0x27b70a85, 0x2e1b2138, 0x4d2c6dfc,	0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, 
+			0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, 
+			0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, 
+			0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,	0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 
+	}; 
+	
+	private static final int BLOCK_SIZE = 32;
+	private boolean forEncryption = false;
+	private static final int ROUNDS = 64;
+	
+	private int[] workingKey = null; // expanded key: corresponds to the message block W in FIPS PUB 180-2
+	
+	public Shacal2Engine()
+	{		
+	}
+	
+	public void reset()
+	{
+	}
+	
+	public String getAlgorithmName()
+	{
+		return "Shacal2";
+	}
+
+	public int getBlockSize()
+	{
+	    return BLOCK_SIZE;
+	}
+
+	public void init(boolean _forEncryption, CipherParameters  params)
+		throws IllegalArgumentException
+	{
+		if (!(params instanceof KeyParameter))
+		{
+			throw new IllegalArgumentException("only simple KeyParameter expected.");
+		}
+		this.forEncryption = _forEncryption;
+		workingKey = new int[64];
+		setKey( ((KeyParameter)params).getKey() );
+	}
+
+	public void setKey(byte[] kb) 
+	{
+		if (kb.length == 0 || kb.length > 64 || kb.length < 16 || kb.length % 8 != 0)
+		{
+			throw new IllegalArgumentException("Shacal2-key must be 16 - 64 bytes and multiple of 8");
+		}
+
+		bytes2ints(kb, workingKey, 0, 0);
+
+		for ( int i = 16; i < 64; i++) 
+		{ // Key-Expansion, implicitly Zero-Padding for 16 > i > kb.length/4
+			workingKey[i] = 
+									( (workingKey[i-2] >>> 17 | workingKey[i-2] << -17) // corresponds to ROTL n(x) of FIPS PUB 180-2
+										^ (workingKey[i-2] >>> 19 | workingKey[i-2] << -19)
+										^ (workingKey[i-2] >>> 10) ) // corresponds to sigma1(x)-Function of FIPS PUB 180-2	    	  
+									+ workingKey[i-7] 		    				
+									+ ( (workingKey[i-15] >>> 7 | workingKey[i-15] << -7) 
+										^ (workingKey[i-15] >>> 18 | workingKey[i-15] << -18) 
+										^ (workingKey[i-15] >>> 3) ) // corresponds to sigma0(x)-Function of FIPS PUB 180-2	    
+									+ workingKey[i-16];
+		}
+	}
+	
+	public void encryptBlock(byte[] in, int inOffset, byte[] out, int outOffset) 
+	{
+		int[] block = new int[BLOCK_SIZE / 4];// corresponds to working variables a,b,c,d,e,f,g,h of FIPS PUB 180-2
+		bytes2ints(in, block, inOffset, 0);
+		
+		for (int i = 0; i < ROUNDS; i++) 
+		{			
+			int tmp =
+                (((block[4] >>> 6) | (block[4] << -6))
+                    ^ ((block[4] >>> 11) | (block[4] << -11))
+                    ^ ((block[4] >>> 25) | (block[4] << -25)))
+                    + ((block[4] & block[5]) ^ ((~block[4]) & block[6]))
+                    + block[7] + K[i] + workingKey[i];  // corresponds to T1 of FIPS PUB 180-2
+			block[7] = block[6];
+			block[6] = block[5];
+			block[5] = block[4];			
+			block[4] = block[3] + tmp;
+			block[3] = block[2];
+			block[2] = block[1];
+			block[1] = block[0];
+			block[0] = tmp
+                + (((block[0] >>> 2) | (block[0] << -2))
+                ^ ((block[0] >>> 13) | (block[0] << -13))
+                ^ ((block[0] >>> 22) | (block[0] << -22)))
+                + ((block[0] & block[2]) ^ (block[0] & block[3]) ^ (block[2] & block[3]));
+			//corresponds to T2 of FIPS PUB 180-2, block[1] and block[2] replaced
+		}		
+		ints2bytes(block, out, outOffset);
+	}
+	
+	public void decryptBlock(byte[] in, int inOffset, byte[] out, int outOffset) 
+	{		
+		int[] block = new int[BLOCK_SIZE / 4];
+		bytes2ints(in, block, inOffset, 0);		
+		for (int i = ROUNDS - 1; i >-1; i--) 
+		{
+            int tmp = block[0] - (((block[1] >>> 2) | (block[1] << -2))
+                ^ ((block[1] >>> 13) | (block[1] << -13))
+                ^ ((block[1] >>> 22) | (block[1] << -22)))
+                - ((block[1] & block[2]) ^ (block[1] & block[3]) ^ (block[2] & block[3]));    // T2
+            block[0] = block[1];
+            block[1] = block[2];
+            block[2] = block[3];
+            block[3] = block[4] - tmp;
+            block[4] = block[5];
+            block[5] = block[6];
+            block[6] = block[7];
+            block[7] = tmp - K[i] - workingKey[i]
+                - (((block[4] >>> 6) | (block[4] << -6))
+                ^ ((block[4] >>> 11) | (block[4] << -11))
+                ^ ((block[4] >>> 25) | (block[4] << -25)))
+                - ((block[4] & block[5]) ^ ((~block[4]) & block[6])); // T1
+        }
+		ints2bytes(block, out, outOffset);
+	}
+
+	public int processBlock(byte[] in, int inOffset, byte[] out, int outOffset)	    
+			throws DataLengthException, IllegalStateException 
+	{
+		if (workingKey == null)
+		{
+			throw new IllegalStateException("Shacal2 not initialised");
+		}
+
+		if ((inOffset + BLOCK_SIZE) > in.length)
+		{
+			throw new DataLengthException("input buffer too short");
+		}
+
+		if ((outOffset + BLOCK_SIZE) > out.length)
+		{
+			throw new OutputLengthException("output buffer too short");
+		}
+
+		if (forEncryption)
+		{
+			encryptBlock(in, inOffset, out, outOffset);
+		}
+		else
+		{    
+			decryptBlock(in, inOffset, out, outOffset);
+		}
+
+		return BLOCK_SIZE;
+	}
+
+    private void bytes2ints(byte[] bytes, int[] block, int bytesPos, int blockPos)
+    {
+        for (int i = blockPos; i < bytes.length / 4; i++)
+        {
+            block[i] = ((bytes[bytesPos++] & 0xFF) << 24)
+                | ((bytes[bytesPos++] & 0xFF) << 16)
+                | ((bytes[bytesPos++] & 0xFF) << 8)
+                | (bytes[bytesPos++] & 0xFF);
+        }
+    }
+
+    private void ints2bytes(int[] block, byte[] out, int pos)
+    {
+        for (int i = 0; i < block.length; i++)
+        {
+            out[pos++] = (byte)(block[i] >>> 24);
+            out[pos++] = (byte)(block[i] >>> 16);
+            out[pos++] = (byte)(block[i] >>> 8);
+            out[pos++] = (byte)block[i];
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
index b09f189..ac65443 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/TEAEngine.java
@@ -105,6 +105,11 @@
     private void setKey(
         byte[]      key)
     {
+        if (key.length != 16) 
+        {
+            throw new IllegalArgumentException("Key size must be 128 bits.");
+        }
+
         _a = bytesToInt(key, 0);
         _b = bytesToInt(key, 4);
         _c = bytesToInt(key, 8);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
new file mode 100644
index 0000000..74bccfe
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/ThreefishEngine.java
@@ -0,0 +1,1494 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.TweakableBlockCipherParameters;
+
+/**
+ * Implementation of the Threefish tweakable large block cipher in 256, 512 and 1024 bit block
+ * sizes.
+ * <p/>
+ * This is the 1.3 version of Threefish defined in the Skein hash function submission to the NIST
+ * SHA-3 competition in October 2010.
+ * <p/>
+ * Threefish was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ * This implementation inlines all round functions, unrolls 8 rounds, and uses 1.2k of static tables
+ * to speed up key schedule injection. <br>
+ * 2 x block size state is retained by each cipher instance.
+ */
+public class ThreefishEngine
+    implements BlockCipher
+{
+    /**
+     * 256 bit block size - Threefish-256
+     */
+    public static final int BLOCKSIZE_256 = 256;
+    /**
+     * 512 bit block size - Threefish-512
+     */
+    public static final int BLOCKSIZE_512 = 512;
+    /**
+     * 1024 bit block size - Threefish-1024
+     */
+    public static final int BLOCKSIZE_1024 = 1024;
+
+    /**
+     * Size of the tweak in bytes (always 128 bit/16 bytes)
+     */
+    private static final int TWEAK_SIZE_BYTES = 16;
+    private static final int TWEAK_SIZE_WORDS = TWEAK_SIZE_BYTES / 8;
+
+    /**
+     * Rounds in Threefish-256
+     */
+    private static final int ROUNDS_256 = 72;
+    /**
+     * Rounds in Threefish-512
+     */
+    private static final int ROUNDS_512 = 72;
+    /**
+     * Rounds in Threefish-1024
+     */
+    private static final int ROUNDS_1024 = 80;
+
+    /**
+     * Max rounds of any of the variants
+     */
+    private static final int MAX_ROUNDS = ROUNDS_1024;
+
+    /**
+     * Key schedule parity constant
+     */
+    private static final long C_240 = 0x1BD11BDAA9FC1A22L;
+
+    /* Pre-calculated modulo arithmetic tables for key schedule lookups */
+    private static int[] MOD9 = new int[MAX_ROUNDS];
+    private static int[] MOD17 = new int[MOD9.length];
+    private static int[] MOD5 = new int[MOD9.length];
+    private static int[] MOD3 = new int[MOD9.length];
+
+    static
+    {
+        for (int i = 0; i < MOD9.length; i++)
+        {
+            MOD17[i] = i % 17;
+            MOD9[i] = i % 9;
+            MOD5[i] = i % 5;
+            MOD3[i] = i % 3;
+        }
+    }
+
+    /**
+     * Block size in bytes
+     */
+    private int blocksizeBytes;
+
+    /**
+     * Block size in 64 bit words
+     */
+    private int blocksizeWords;
+
+    /**
+     * Buffer for byte oriented processBytes to call internal word API
+     */
+    private long[] currentBlock;
+
+    /**
+     * Tweak bytes (2 byte t1,t2, calculated t3 and repeat of t1,t2 for modulo free lookup
+     */
+    private long[] t = new long[5];
+
+    /**
+     * Key schedule words
+     */
+    private long[] kw;
+
+    /**
+     * The internal cipher implementation (varies by blocksize)
+     */
+    private ThreefishCipher cipher;
+
+    private boolean forEncryption;
+
+    /**
+     * Constructs a new Threefish cipher, with a specified block size.
+     *
+     * @param blocksizeBits the block size in bits, one of {@link #BLOCKSIZE_256}, {@link #BLOCKSIZE_512},
+     *                      {@link #BLOCKSIZE_1024}.
+     */
+    public ThreefishEngine(final int blocksizeBits)
+    {
+        this.blocksizeBytes = (blocksizeBits / 8);
+        this.blocksizeWords = (this.blocksizeBytes / 8);
+        this.currentBlock = new long[blocksizeWords];
+
+        /*
+         * Provide room for original key words, extended key word and repeat of key words for modulo
+         * free lookup of key schedule words.
+         */
+        this.kw = new long[2 * blocksizeWords + 1];
+
+        switch (blocksizeBits)
+        {
+        case BLOCKSIZE_256:
+            cipher = new Threefish256Cipher(kw, t);
+            break;
+        case BLOCKSIZE_512:
+            cipher = new Threefish512Cipher(kw, t);
+            break;
+        case BLOCKSIZE_1024:
+            cipher = new Threefish1024Cipher(kw, t);
+            break;
+        default:
+            throw new IllegalArgumentException(
+                "Invalid blocksize - Threefish is defined with block size of 256, 512, or 1024 bits");
+        }
+    }
+
+    /**
+     * Initialise the engine.
+     *
+     * @param params an instance of {@link TweakableBlockCipherParameters}, or {@link KeyParameter} (to
+     *               use a 0 tweak)
+     */
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        final byte[] keyBytes;
+        final byte[] tweakBytes;
+
+        if (params instanceof TweakableBlockCipherParameters)
+        {
+            TweakableBlockCipherParameters tParams = (TweakableBlockCipherParameters)params;
+            keyBytes = tParams.getKey().getKey();
+            tweakBytes = tParams.getTweak();
+        }
+        else if (params instanceof KeyParameter)
+        {
+            keyBytes = ((KeyParameter)params).getKey();
+            tweakBytes = null;
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid parameter passed to Threefish init - "
+                + params.getClass().getName());
+        }
+
+        long[] keyWords = null;
+        long[] tweakWords = null;
+
+        if (keyBytes != null)
+        {
+            if (keyBytes.length != this.blocksizeBytes)
+            {
+                throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeBytes
+                    + " bytes)");
+            }
+            keyWords = new long[blocksizeWords];
+            for (int i = 0; i < keyWords.length; i++)
+            {
+                keyWords[i] = bytesToWord(keyBytes, i * 8);
+            }
+        }
+        if (tweakBytes != null)
+        {
+            if (tweakBytes.length != TWEAK_SIZE_BYTES)
+            {
+                throw new IllegalArgumentException("Threefish tweak must be " + TWEAK_SIZE_BYTES + " bytes");
+            }
+            tweakWords = new long[]{bytesToWord(tweakBytes, 0), bytesToWord(tweakBytes, 8)};
+        }
+        init(forEncryption, keyWords, tweakWords);
+    }
+
+    /**
+     * Initialise the engine, specifying the key and tweak directly.
+     *
+     * @param forEncryption the cipher mode.
+     * @param key           the words of the key, or <code>null</code> to use the current key.
+     * @param tweak         the 2 word (128 bit) tweak, or <code>null</code> to use the current tweak.
+     */
+    public void init(boolean forEncryption, final long[] key, final long[] tweak)
+    {
+        this.forEncryption = forEncryption;
+        if (key != null)
+        {
+            setKey(key);
+        }
+        if (tweak != null)
+        {
+            setTweak(tweak);
+        }
+    }
+
+    private void setKey(long[] key)
+    {
+        if (key.length != this.blocksizeWords)
+        {
+            throw new IllegalArgumentException("Threefish key must be same size as block (" + blocksizeWords
+                + " words)");
+        }
+
+        /*
+         * Full subkey schedule is deferred to execution to avoid per cipher overhead (10k for 512,
+         * 20k for 1024).
+         * 
+         * Key and tweak word sequences are repeated, and static MOD17/MOD9/MOD5/MOD3 calculations
+         * used, to avoid expensive mod computations during cipher operation.
+         */
+
+        long knw = C_240;
+        for (int i = 0; i < blocksizeWords; i++)
+        {
+            kw[i] = key[i];
+            knw = knw ^ kw[i];
+        }
+        kw[blocksizeWords] = knw;
+        System.arraycopy(kw, 0, kw, blocksizeWords + 1, blocksizeWords);
+    }
+
+    private void setTweak(long[] tweak)
+    {
+        if (tweak.length != TWEAK_SIZE_WORDS)
+        {
+            throw new IllegalArgumentException("Tweak must be " + TWEAK_SIZE_WORDS + " words.");
+        }
+
+        /*
+         * Tweak schedule partially repeated to avoid mod computations during cipher operation
+         */
+        t[0] = tweak[0];
+        t[1] = tweak[1];
+        t[2] = t[0] ^ t[1];
+        t[3] = t[0];
+        t[4] = t[1];
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Threefish-" + (blocksizeBytes * 8);
+    }
+
+    public int getBlockSize()
+    {
+        return blocksizeBytes;
+    }
+
+    public void reset()
+    {
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException,
+        IllegalStateException
+    {
+        if ((outOff + blocksizeBytes) > out.length)
+        {
+            throw new DataLengthException("Output buffer too short");
+        }
+
+        if ((inOff + blocksizeBytes) > in.length)
+        {
+            throw new DataLengthException("Input buffer too short");
+        }
+
+        for (int i = 0; i < blocksizeBytes; i += 8)
+        {
+            currentBlock[i >> 3] = bytesToWord(in, inOff + i);
+        }
+        processBlock(this.currentBlock, this.currentBlock);
+        for (int i = 0; i < blocksizeBytes; i += 8)
+        {
+            wordToBytes(this.currentBlock[i >> 3], out, outOff + i);
+        }
+
+        return blocksizeBytes;
+    }
+
+    /**
+     * Process a block of data represented as 64 bit words.
+     *
+     * @param in  a block sized buffer of words to process.
+     * @param out a block sized buffer of words to receive the output of the operation.
+     * @return the number of 8 byte words processed (which will be the same as the block size).
+     * @throws DataLengthException if either the input or output is not block sized.
+     * @throws IllegalStateException if this engine is not initialised.
+     */
+    public int processBlock(long[] in, long[] out)
+        throws DataLengthException, IllegalStateException
+    {
+        if (kw[blocksizeWords] == 0)
+        {
+            throw new IllegalStateException("Threefish engine not initialised");
+        }
+
+        if (in.length != blocksizeWords)
+        {
+            throw new DataLengthException("Input buffer too short");
+        }
+        if (out.length != blocksizeWords)
+        {
+            throw new DataLengthException("Output buffer too short");
+        }
+
+        if (forEncryption)
+        {
+            cipher.encryptBlock(in, out);
+        }
+        else
+        {
+            cipher.decryptBlock(in, out);
+        }
+
+        return blocksizeWords;
+    }
+
+    /**
+     * Read a single 64 bit word from input in LSB first order.
+     */
+    // At least package protected for efficient access from inner class
+    public static long bytesToWord(final byte[] bytes, final int off)
+    {
+        if ((off + 8) > bytes.length)
+        {
+            // Help the JIT avoid index checks
+            throw new IllegalArgumentException();
+        }
+
+        long word = 0;
+        int index = off;
+
+        word = (bytes[index++] & 0xffL);
+        word |= (bytes[index++] & 0xffL) << 8;
+        word |= (bytes[index++] & 0xffL) << 16;
+        word |= (bytes[index++] & 0xffL) << 24;
+        word |= (bytes[index++] & 0xffL) << 32;
+        word |= (bytes[index++] & 0xffL) << 40;
+        word |= (bytes[index++] & 0xffL) << 48;
+        word |= (bytes[index++] & 0xffL) << 56;
+
+        return word;
+    }
+
+    /**
+     * Write a 64 bit word to output in LSB first order.
+     */
+    // At least package protected for efficient access from inner class
+    public static void wordToBytes(final long word, final byte[] bytes, final int off)
+    {
+        if ((off + 8) > bytes.length)
+        {
+            // Help the JIT avoid index checks
+            throw new IllegalArgumentException();
+        }
+        int index = off;
+
+        bytes[index++] = (byte)word;
+        bytes[index++] = (byte)(word >> 8);
+        bytes[index++] = (byte)(word >> 16);
+        bytes[index++] = (byte)(word >> 24);
+        bytes[index++] = (byte)(word >> 32);
+        bytes[index++] = (byte)(word >> 40);
+        bytes[index++] = (byte)(word >> 48);
+        bytes[index++] = (byte)(word >> 56);
+    }
+
+    /**
+     * Rotate left + xor part of the mix operation.
+     */
+    // Package protected for efficient access from inner class
+    static long rotlXor(long x, int n, long xor)
+    {
+        return ((x << n) | (x >>> -n)) ^ xor;
+    }
+
+    /**
+     * Rotate xor + rotate right part of the unmix operation.
+     */
+    // Package protected for efficient access from inner class
+    static long xorRotr(long x, int n, long xor)
+    {
+        long xored = x ^ xor;
+        return (xored >>> n) | (xored << -n);
+    }
+
+    private static abstract class ThreefishCipher
+    {
+        /**
+         * The extended + repeated tweak words
+         */
+        protected final long[] t;
+        /**
+         * The extended + repeated key words
+         */
+        protected final long[] kw;
+
+        protected ThreefishCipher(final long[] kw, final long[] t)
+        {
+            this.kw = kw;
+            this.t = t;
+        }
+
+        abstract void encryptBlock(long[] block, long[] out);
+
+        abstract void decryptBlock(long[] block, long[] out);
+
+    }
+
+    private static final class Threefish256Cipher
+        extends ThreefishCipher
+    {
+        /**
+         * Mix rotation constants defined in Skein 1.3 specification
+         */
+        private static final int ROTATION_0_0 = 14, ROTATION_0_1 = 16;
+        private static final int ROTATION_1_0 = 52, ROTATION_1_1 = 57;
+        private static final int ROTATION_2_0 = 23, ROTATION_2_1 = 40;
+        private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 37;
+
+        private static final int ROTATION_4_0 = 25, ROTATION_4_1 = 33;
+        private static final int ROTATION_5_0 = 46, ROTATION_5_1 = 12;
+        private static final int ROTATION_6_0 = 58, ROTATION_6_1 = 22;
+        private static final int ROTATION_7_0 = 32, ROTATION_7_1 = 32;
+
+        public Threefish256Cipher(long[] kw, long[] t)
+        {
+            super(kw, t);
+        }
+
+        void encryptBlock(long[] block, long[] out)
+        {
+            final long[] kw = this.kw;
+            final long[] t = this.t;
+            final int[] mod5 = MOD5;
+            final int[] mod3 = MOD3;
+
+            /* Help the JIT avoid index bounds checks */
+            if (kw.length != 9)
+            {
+                throw new IllegalArgumentException();
+            }
+            if (t.length != 5)
+            {
+                throw new IllegalArgumentException();
+            }
+
+            /*
+             * Read 4 words of plaintext data, not using arrays for cipher state
+             */
+            long b0 = block[0];
+            long b1 = block[1];
+            long b2 = block[2];
+            long b3 = block[3];
+
+            /*
+             * First subkey injection.
+             */
+            b0 += kw[0];
+            b1 += kw[1] + t[0];
+            b2 += kw[2] + t[1];
+            b3 += kw[3];
+
+            /*
+             * Rounds loop, unrolled to 8 rounds per iteration.
+             * 
+             * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+             * inlining of the permutations, which cycle every of 2 rounds (avoiding array
+             * index/lookup).
+             * 
+             * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+             * inlining constant rotation values (avoiding array index/lookup).
+             */
+
+            for (int d = 1; d < (ROUNDS_256 / 4); d += 2)
+            {
+                final int dm5 = mod5[d];
+                final int dm3 = mod3[d];
+
+                /*
+                 * 4 rounds of mix and permute.
+                 * 
+                 * Permute schedule has a 2 round cycle, so permutes are inlined in the mix
+                 * operations in each 4 round block.
+                 */
+                b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+
+                b3 = rotlXor(b3, ROTATION_1_0, b0 += b3);
+                b1 = rotlXor(b1, ROTATION_1_1, b2 += b1);
+
+                b1 = rotlXor(b1, ROTATION_2_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_2_1, b2 += b3);
+
+                b3 = rotlXor(b3, ROTATION_3_0, b0 += b3);
+                b1 = rotlXor(b1, ROTATION_3_1, b2 += b1);
+
+                /*
+                 * Subkey injection for first 4 rounds.
+                 */
+                b0 += kw[dm5];
+                b1 += kw[dm5 + 1] + t[dm3];
+                b2 += kw[dm5 + 2] + t[dm3 + 1];
+                b3 += kw[dm5 + 3] + d;
+
+                /*
+                 * 4 more rounds of mix/permute
+                 */
+                b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+
+                b3 = rotlXor(b3, ROTATION_5_0, b0 += b3);
+                b1 = rotlXor(b1, ROTATION_5_1, b2 += b1);
+
+                b1 = rotlXor(b1, ROTATION_6_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_6_1, b2 += b3);
+
+                b3 = rotlXor(b3, ROTATION_7_0, b0 += b3);
+                b1 = rotlXor(b1, ROTATION_7_1, b2 += b1);
+
+                /*
+                 * Subkey injection for next 4 rounds.
+                 */
+                b0 += kw[dm5 + 1];
+                b1 += kw[dm5 + 2] + t[dm3 + 1];
+                b2 += kw[dm5 + 3] + t[dm3 + 2];
+                b3 += kw[dm5 + 4] + d + 1;
+            }
+
+            /*
+             * Output cipher state.
+             */
+            out[0] = b0;
+            out[1] = b1;
+            out[2] = b2;
+            out[3] = b3;
+        }
+
+        void decryptBlock(long[] block, long[] state)
+        {
+            final long[] kw = this.kw;
+            final long[] t = this.t;
+            final int[] mod5 = MOD5;
+            final int[] mod3 = MOD3;
+
+            /* Help the JIT avoid index bounds checks */
+            if (kw.length != 9)
+            {
+                throw new IllegalArgumentException();
+            }
+            if (t.length != 5)
+            {
+                throw new IllegalArgumentException();
+            }
+
+            long b0 = block[0];
+            long b1 = block[1];
+            long b2 = block[2];
+            long b3 = block[3];
+
+            for (int d = (ROUNDS_256 / 4) - 1; d >= 1; d -= 2)
+            {
+                final int dm5 = mod5[d];
+                final int dm3 = mod3[d];
+
+                /* Reverse key injection for second 4 rounds */
+                b0 -= kw[dm5 + 1];
+                b1 -= kw[dm5 + 2] + t[dm3 + 1];
+                b2 -= kw[dm5 + 3] + t[dm3 + 2];
+                b3 -= kw[dm5 + 4] + d + 1;
+
+                /* Reverse second 4 mix/permute rounds */
+
+                b3 = xorRotr(b3, ROTATION_7_0, b0);
+                b0 -= b3;
+                b1 = xorRotr(b1, ROTATION_7_1, b2);
+                b2 -= b1;
+
+                b1 = xorRotr(b1, ROTATION_6_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_6_1, b2);
+                b2 -= b3;
+
+                b3 = xorRotr(b3, ROTATION_5_0, b0);
+                b0 -= b3;
+                b1 = xorRotr(b1, ROTATION_5_1, b2);
+                b2 -= b1;
+
+                b1 = xorRotr(b1, ROTATION_4_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_4_1, b2);
+                b2 -= b3;
+
+                /* Reverse key injection for first 4 rounds */
+                b0 -= kw[dm5];
+                b1 -= kw[dm5 + 1] + t[dm3];
+                b2 -= kw[dm5 + 2] + t[dm3 + 1];
+                b3 -= kw[dm5 + 3] + d;
+
+                /* Reverse first 4 mix/permute rounds */
+                b3 = xorRotr(b3, ROTATION_3_0, b0);
+                b0 -= b3;
+                b1 = xorRotr(b1, ROTATION_3_1, b2);
+                b2 -= b1;
+
+                b1 = xorRotr(b1, ROTATION_2_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_2_1, b2);
+                b2 -= b3;
+
+                b3 = xorRotr(b3, ROTATION_1_0, b0);
+                b0 -= b3;
+                b1 = xorRotr(b1, ROTATION_1_1, b2);
+                b2 -= b1;
+
+                b1 = xorRotr(b1, ROTATION_0_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_0_1, b2);
+                b2 -= b3;
+            }
+
+            /*
+             * First subkey uninjection.
+             */
+            b0 -= kw[0];
+            b1 -= kw[1] + t[0];
+            b2 -= kw[2] + t[1];
+            b3 -= kw[3];
+
+            /*
+             * Output cipher state.
+             */
+            state[0] = b0;
+            state[1] = b1;
+            state[2] = b2;
+            state[3] = b3;
+        }
+
+    }
+
+    private static final class Threefish512Cipher
+        extends ThreefishCipher
+    {
+        /**
+         * Mix rotation constants defined in Skein 1.3 specification
+         */
+        private static final int ROTATION_0_0 = 46, ROTATION_0_1 = 36, ROTATION_0_2 = 19, ROTATION_0_3 = 37;
+        private static final int ROTATION_1_0 = 33, ROTATION_1_1 = 27, ROTATION_1_2 = 14, ROTATION_1_3 = 42;
+        private static final int ROTATION_2_0 = 17, ROTATION_2_1 = 49, ROTATION_2_2 = 36, ROTATION_2_3 = 39;
+        private static final int ROTATION_3_0 = 44, ROTATION_3_1 = 9, ROTATION_3_2 = 54, ROTATION_3_3 = 56;
+
+        private static final int ROTATION_4_0 = 39, ROTATION_4_1 = 30, ROTATION_4_2 = 34, ROTATION_4_3 = 24;
+        private static final int ROTATION_5_0 = 13, ROTATION_5_1 = 50, ROTATION_5_2 = 10, ROTATION_5_3 = 17;
+        private static final int ROTATION_6_0 = 25, ROTATION_6_1 = 29, ROTATION_6_2 = 39, ROTATION_6_3 = 43;
+        private static final int ROTATION_7_0 = 8, ROTATION_7_1 = 35, ROTATION_7_2 = 56, ROTATION_7_3 = 22;
+
+        protected Threefish512Cipher(long[] kw, long[] t)
+        {
+            super(kw, t);
+        }
+
+        public void encryptBlock(long[] block, long[] out)
+        {
+            final long[] kw = this.kw;
+            final long[] t = this.t;
+            final int[] mod9 = MOD9;
+            final int[] mod3 = MOD3;
+
+            /* Help the JIT avoid index bounds checks */
+            if (kw.length != 17)
+            {
+                throw new IllegalArgumentException();
+            }
+            if (t.length != 5)
+            {
+                throw new IllegalArgumentException();
+            }
+
+            /*
+             * Read 8 words of plaintext data, not using arrays for cipher state
+             */
+            long b0 = block[0];
+            long b1 = block[1];
+            long b2 = block[2];
+            long b3 = block[3];
+            long b4 = block[4];
+            long b5 = block[5];
+            long b6 = block[6];
+            long b7 = block[7];
+
+            /*
+             * First subkey injection.
+             */
+            b0 += kw[0];
+            b1 += kw[1];
+            b2 += kw[2];
+            b3 += kw[3];
+            b4 += kw[4];
+            b5 += kw[5] + t[0];
+            b6 += kw[6] + t[1];
+            b7 += kw[7];
+
+            /*
+             * Rounds loop, unrolled to 8 rounds per iteration.
+             * 
+             * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+             * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+             * index/lookup).
+             * 
+             * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+             * inlining constant rotation values (avoiding array index/lookup).
+             */
+
+            for (int d = 1; d < (ROUNDS_512 / 4); d += 2)
+            {
+                final int dm9 = mod9[d];
+                final int dm3 = mod3[d];
+
+                /*
+                 * 4 rounds of mix and permute.
+                 * 
+                 * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+                 * operations in each 4 round block.
+                 */
+                b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+                b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+                b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+
+                b1 = rotlXor(b1, ROTATION_1_0, b2 += b1);
+                b7 = rotlXor(b7, ROTATION_1_1, b4 += b7);
+                b5 = rotlXor(b5, ROTATION_1_2, b6 += b5);
+                b3 = rotlXor(b3, ROTATION_1_3, b0 += b3);
+
+                b1 = rotlXor(b1, ROTATION_2_0, b4 += b1);
+                b3 = rotlXor(b3, ROTATION_2_1, b6 += b3);
+                b5 = rotlXor(b5, ROTATION_2_2, b0 += b5);
+                b7 = rotlXor(b7, ROTATION_2_3, b2 += b7);
+
+                b1 = rotlXor(b1, ROTATION_3_0, b6 += b1);
+                b7 = rotlXor(b7, ROTATION_3_1, b0 += b7);
+                b5 = rotlXor(b5, ROTATION_3_2, b2 += b5);
+                b3 = rotlXor(b3, ROTATION_3_3, b4 += b3);
+
+                /*
+                 * Subkey injection for first 4 rounds.
+                 */
+                b0 += kw[dm9];
+                b1 += kw[dm9 + 1];
+                b2 += kw[dm9 + 2];
+                b3 += kw[dm9 + 3];
+                b4 += kw[dm9 + 4];
+                b5 += kw[dm9 + 5] + t[dm3];
+                b6 += kw[dm9 + 6] + t[dm3 + 1];
+                b7 += kw[dm9 + 7] + d;
+
+                /*
+                 * 4 more rounds of mix/permute
+                 */
+                b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+                b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+                b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+
+                b1 = rotlXor(b1, ROTATION_5_0, b2 += b1);
+                b7 = rotlXor(b7, ROTATION_5_1, b4 += b7);
+                b5 = rotlXor(b5, ROTATION_5_2, b6 += b5);
+                b3 = rotlXor(b3, ROTATION_5_3, b0 += b3);
+
+                b1 = rotlXor(b1, ROTATION_6_0, b4 += b1);
+                b3 = rotlXor(b3, ROTATION_6_1, b6 += b3);
+                b5 = rotlXor(b5, ROTATION_6_2, b0 += b5);
+                b7 = rotlXor(b7, ROTATION_6_3, b2 += b7);
+
+                b1 = rotlXor(b1, ROTATION_7_0, b6 += b1);
+                b7 = rotlXor(b7, ROTATION_7_1, b0 += b7);
+                b5 = rotlXor(b5, ROTATION_7_2, b2 += b5);
+                b3 = rotlXor(b3, ROTATION_7_3, b4 += b3);
+
+                /*
+                 * Subkey injection for next 4 rounds.
+                 */
+                b0 += kw[dm9 + 1];
+                b1 += kw[dm9 + 2];
+                b2 += kw[dm9 + 3];
+                b3 += kw[dm9 + 4];
+                b4 += kw[dm9 + 5];
+                b5 += kw[dm9 + 6] + t[dm3 + 1];
+                b6 += kw[dm9 + 7] + t[dm3 + 2];
+                b7 += kw[dm9 + 8] + d + 1;
+            }
+
+            /*
+             * Output cipher state.
+             */
+            out[0] = b0;
+            out[1] = b1;
+            out[2] = b2;
+            out[3] = b3;
+            out[4] = b4;
+            out[5] = b5;
+            out[6] = b6;
+            out[7] = b7;
+        }
+
+        public void decryptBlock(long[] block, long[] state)
+        {
+            final long[] kw = this.kw;
+            final long[] t = this.t;
+            final int[] mod9 = MOD9;
+            final int[] mod3 = MOD3;
+
+            /* Help the JIT avoid index bounds checks */
+            if (kw.length != 17)
+            {
+                throw new IllegalArgumentException();
+            }
+            if (t.length != 5)
+            {
+                throw new IllegalArgumentException();
+            }
+
+            long b0 = block[0];
+            long b1 = block[1];
+            long b2 = block[2];
+            long b3 = block[3];
+            long b4 = block[4];
+            long b5 = block[5];
+            long b6 = block[6];
+            long b7 = block[7];
+
+            for (int d = (ROUNDS_512 / 4) - 1; d >= 1; d -= 2)
+            {
+                final int dm9 = mod9[d];
+                final int dm3 = mod3[d];
+
+                /* Reverse key injection for second 4 rounds */
+                b0 -= kw[dm9 + 1];
+                b1 -= kw[dm9 + 2];
+                b2 -= kw[dm9 + 3];
+                b3 -= kw[dm9 + 4];
+                b4 -= kw[dm9 + 5];
+                b5 -= kw[dm9 + 6] + t[dm3 + 1];
+                b6 -= kw[dm9 + 7] + t[dm3 + 2];
+                b7 -= kw[dm9 + 8] + d + 1;
+
+                /* Reverse second 4 mix/permute rounds */
+
+                b1 = xorRotr(b1, ROTATION_7_0, b6);
+                b6 -= b1;
+                b7 = xorRotr(b7, ROTATION_7_1, b0);
+                b0 -= b7;
+                b5 = xorRotr(b5, ROTATION_7_2, b2);
+                b2 -= b5;
+                b3 = xorRotr(b3, ROTATION_7_3, b4);
+                b4 -= b3;
+
+                b1 = xorRotr(b1, ROTATION_6_0, b4);
+                b4 -= b1;
+                b3 = xorRotr(b3, ROTATION_6_1, b6);
+                b6 -= b3;
+                b5 = xorRotr(b5, ROTATION_6_2, b0);
+                b0 -= b5;
+                b7 = xorRotr(b7, ROTATION_6_3, b2);
+                b2 -= b7;
+
+                b1 = xorRotr(b1, ROTATION_5_0, b2);
+                b2 -= b1;
+                b7 = xorRotr(b7, ROTATION_5_1, b4);
+                b4 -= b7;
+                b5 = xorRotr(b5, ROTATION_5_2, b6);
+                b6 -= b5;
+                b3 = xorRotr(b3, ROTATION_5_3, b0);
+                b0 -= b3;
+
+                b1 = xorRotr(b1, ROTATION_4_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_4_1, b2);
+                b2 -= b3;
+                b5 = xorRotr(b5, ROTATION_4_2, b4);
+                b4 -= b5;
+                b7 = xorRotr(b7, ROTATION_4_3, b6);
+                b6 -= b7;
+
+                /* Reverse key injection for first 4 rounds */
+                b0 -= kw[dm9];
+                b1 -= kw[dm9 + 1];
+                b2 -= kw[dm9 + 2];
+                b3 -= kw[dm9 + 3];
+                b4 -= kw[dm9 + 4];
+                b5 -= kw[dm9 + 5] + t[dm3];
+                b6 -= kw[dm9 + 6] + t[dm3 + 1];
+                b7 -= kw[dm9 + 7] + d;
+
+                /* Reverse first 4 mix/permute rounds */
+                b1 = xorRotr(b1, ROTATION_3_0, b6);
+                b6 -= b1;
+                b7 = xorRotr(b7, ROTATION_3_1, b0);
+                b0 -= b7;
+                b5 = xorRotr(b5, ROTATION_3_2, b2);
+                b2 -= b5;
+                b3 = xorRotr(b3, ROTATION_3_3, b4);
+                b4 -= b3;
+
+                b1 = xorRotr(b1, ROTATION_2_0, b4);
+                b4 -= b1;
+                b3 = xorRotr(b3, ROTATION_2_1, b6);
+                b6 -= b3;
+                b5 = xorRotr(b5, ROTATION_2_2, b0);
+                b0 -= b5;
+                b7 = xorRotr(b7, ROTATION_2_3, b2);
+                b2 -= b7;
+
+                b1 = xorRotr(b1, ROTATION_1_0, b2);
+                b2 -= b1;
+                b7 = xorRotr(b7, ROTATION_1_1, b4);
+                b4 -= b7;
+                b5 = xorRotr(b5, ROTATION_1_2, b6);
+                b6 -= b5;
+                b3 = xorRotr(b3, ROTATION_1_3, b0);
+                b0 -= b3;
+
+                b1 = xorRotr(b1, ROTATION_0_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_0_1, b2);
+                b2 -= b3;
+                b5 = xorRotr(b5, ROTATION_0_2, b4);
+                b4 -= b5;
+                b7 = xorRotr(b7, ROTATION_0_3, b6);
+                b6 -= b7;
+            }
+
+            /*
+             * First subkey uninjection.
+             */
+            b0 -= kw[0];
+            b1 -= kw[1];
+            b2 -= kw[2];
+            b3 -= kw[3];
+            b4 -= kw[4];
+            b5 -= kw[5] + t[0];
+            b6 -= kw[6] + t[1];
+            b7 -= kw[7];
+
+            /*
+             * Output cipher state.
+             */
+            state[0] = b0;
+            state[1] = b1;
+            state[2] = b2;
+            state[3] = b3;
+            state[4] = b4;
+            state[5] = b5;
+            state[6] = b6;
+            state[7] = b7;
+        }
+    }
+
+    private static final class Threefish1024Cipher
+        extends ThreefishCipher
+    {
+        /**
+         * Mix rotation constants defined in Skein 1.3 specification
+         */
+        private static final int ROTATION_0_0 = 24, ROTATION_0_1 = 13, ROTATION_0_2 = 8, ROTATION_0_3 = 47;
+        private static final int ROTATION_0_4 = 8, ROTATION_0_5 = 17, ROTATION_0_6 = 22, ROTATION_0_7 = 37;
+        private static final int ROTATION_1_0 = 38, ROTATION_1_1 = 19, ROTATION_1_2 = 10, ROTATION_1_3 = 55;
+        private static final int ROTATION_1_4 = 49, ROTATION_1_5 = 18, ROTATION_1_6 = 23, ROTATION_1_7 = 52;
+        private static final int ROTATION_2_0 = 33, ROTATION_2_1 = 4, ROTATION_2_2 = 51, ROTATION_2_3 = 13;
+        private static final int ROTATION_2_4 = 34, ROTATION_2_5 = 41, ROTATION_2_6 = 59, ROTATION_2_7 = 17;
+        private static final int ROTATION_3_0 = 5, ROTATION_3_1 = 20, ROTATION_3_2 = 48, ROTATION_3_3 = 41;
+        private static final int ROTATION_3_4 = 47, ROTATION_3_5 = 28, ROTATION_3_6 = 16, ROTATION_3_7 = 25;
+
+        private static final int ROTATION_4_0 = 41, ROTATION_4_1 = 9, ROTATION_4_2 = 37, ROTATION_4_3 = 31;
+        private static final int ROTATION_4_4 = 12, ROTATION_4_5 = 47, ROTATION_4_6 = 44, ROTATION_4_7 = 30;
+        private static final int ROTATION_5_0 = 16, ROTATION_5_1 = 34, ROTATION_5_2 = 56, ROTATION_5_3 = 51;
+        private static final int ROTATION_5_4 = 4, ROTATION_5_5 = 53, ROTATION_5_6 = 42, ROTATION_5_7 = 41;
+        private static final int ROTATION_6_0 = 31, ROTATION_6_1 = 44, ROTATION_6_2 = 47, ROTATION_6_3 = 46;
+        private static final int ROTATION_6_4 = 19, ROTATION_6_5 = 42, ROTATION_6_6 = 44, ROTATION_6_7 = 25;
+        private static final int ROTATION_7_0 = 9, ROTATION_7_1 = 48, ROTATION_7_2 = 35, ROTATION_7_3 = 52;
+        private static final int ROTATION_7_4 = 23, ROTATION_7_5 = 31, ROTATION_7_6 = 37, ROTATION_7_7 = 20;
+
+        public Threefish1024Cipher(long[] kw, long[] t)
+        {
+            super(kw, t);
+        }
+
+        void encryptBlock(long[] block, long[] out)
+        {
+            final long[] kw = this.kw;
+            final long[] t = this.t;
+            final int[] mod17 = MOD17;
+            final int[] mod3 = MOD3;
+
+            /* Help the JIT avoid index bounds checks */
+            if (kw.length != 33)
+            {
+                throw new IllegalArgumentException();
+            }
+            if (t.length != 5)
+            {
+                throw new IllegalArgumentException();
+            }
+
+            /*
+             * Read 16 words of plaintext data, not using arrays for cipher state
+             */
+            long b0 = block[0];
+            long b1 = block[1];
+            long b2 = block[2];
+            long b3 = block[3];
+            long b4 = block[4];
+            long b5 = block[5];
+            long b6 = block[6];
+            long b7 = block[7];
+            long b8 = block[8];
+            long b9 = block[9];
+            long b10 = block[10];
+            long b11 = block[11];
+            long b12 = block[12];
+            long b13 = block[13];
+            long b14 = block[14];
+            long b15 = block[15];
+
+            /*
+             * First subkey injection.
+             */
+            b0 += kw[0];
+            b1 += kw[1];
+            b2 += kw[2];
+            b3 += kw[3];
+            b4 += kw[4];
+            b5 += kw[5];
+            b6 += kw[6];
+            b7 += kw[7];
+            b8 += kw[8];
+            b9 += kw[9];
+            b10 += kw[10];
+            b11 += kw[11];
+            b12 += kw[12];
+            b13 += kw[13] + t[0];
+            b14 += kw[14] + t[1];
+            b15 += kw[15];
+
+            /*
+             * Rounds loop, unrolled to 8 rounds per iteration.
+             * 
+             * Unrolling to multiples of 4 avoids the mod 4 check for key injection, and allows
+             * inlining of the permutations, which cycle every of 4 rounds (avoiding array
+             * index/lookup).
+             * 
+             * Unrolling to multiples of 8 avoids the mod 8 rotation constant lookup, and allows
+             * inlining constant rotation values (avoiding array index/lookup).
+             */
+
+            for (int d = 1; d < (ROUNDS_1024 / 4); d += 2)
+            {
+                final int dm17 = mod17[d];
+                final int dm3 = mod3[d];
+
+                /*
+                 * 4 rounds of mix and permute.
+                 * 
+                 * Permute schedule has a 4 round cycle, so permutes are inlined in the mix
+                 * operations in each 4 round block.
+                 */
+                b1 = rotlXor(b1, ROTATION_0_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_0_1, b2 += b3);
+                b5 = rotlXor(b5, ROTATION_0_2, b4 += b5);
+                b7 = rotlXor(b7, ROTATION_0_3, b6 += b7);
+                b9 = rotlXor(b9, ROTATION_0_4, b8 += b9);
+                b11 = rotlXor(b11, ROTATION_0_5, b10 += b11);
+                b13 = rotlXor(b13, ROTATION_0_6, b12 += b13);
+                b15 = rotlXor(b15, ROTATION_0_7, b14 += b15);
+
+                b9 = rotlXor(b9, ROTATION_1_0, b0 += b9);
+                b13 = rotlXor(b13, ROTATION_1_1, b2 += b13);
+                b11 = rotlXor(b11, ROTATION_1_2, b6 += b11);
+                b15 = rotlXor(b15, ROTATION_1_3, b4 += b15);
+                b7 = rotlXor(b7, ROTATION_1_4, b10 += b7);
+                b3 = rotlXor(b3, ROTATION_1_5, b12 += b3);
+                b5 = rotlXor(b5, ROTATION_1_6, b14 += b5);
+                b1 = rotlXor(b1, ROTATION_1_7, b8 += b1);
+
+                b7 = rotlXor(b7, ROTATION_2_0, b0 += b7);
+                b5 = rotlXor(b5, ROTATION_2_1, b2 += b5);
+                b3 = rotlXor(b3, ROTATION_2_2, b4 += b3);
+                b1 = rotlXor(b1, ROTATION_2_3, b6 += b1);
+                b15 = rotlXor(b15, ROTATION_2_4, b12 += b15);
+                b13 = rotlXor(b13, ROTATION_2_5, b14 += b13);
+                b11 = rotlXor(b11, ROTATION_2_6, b8 += b11);
+                b9 = rotlXor(b9, ROTATION_2_7, b10 += b9);
+
+                b15 = rotlXor(b15, ROTATION_3_0, b0 += b15);
+                b11 = rotlXor(b11, ROTATION_3_1, b2 += b11);
+                b13 = rotlXor(b13, ROTATION_3_2, b6 += b13);
+                b9 = rotlXor(b9, ROTATION_3_3, b4 += b9);
+                b1 = rotlXor(b1, ROTATION_3_4, b14 += b1);
+                b5 = rotlXor(b5, ROTATION_3_5, b8 += b5);
+                b3 = rotlXor(b3, ROTATION_3_6, b10 += b3);
+                b7 = rotlXor(b7, ROTATION_3_7, b12 += b7);
+
+                /*
+                 * Subkey injection for first 4 rounds.
+                 */
+                b0 += kw[dm17];
+                b1 += kw[dm17 + 1];
+                b2 += kw[dm17 + 2];
+                b3 += kw[dm17 + 3];
+                b4 += kw[dm17 + 4];
+                b5 += kw[dm17 + 5];
+                b6 += kw[dm17 + 6];
+                b7 += kw[dm17 + 7];
+                b8 += kw[dm17 + 8];
+                b9 += kw[dm17 + 9];
+                b10 += kw[dm17 + 10];
+                b11 += kw[dm17 + 11];
+                b12 += kw[dm17 + 12];
+                b13 += kw[dm17 + 13] + t[dm3];
+                b14 += kw[dm17 + 14] + t[dm3 + 1];
+                b15 += kw[dm17 + 15] + d;
+
+                /*
+                 * 4 more rounds of mix/permute
+                 */
+                b1 = rotlXor(b1, ROTATION_4_0, b0 += b1);
+                b3 = rotlXor(b3, ROTATION_4_1, b2 += b3);
+                b5 = rotlXor(b5, ROTATION_4_2, b4 += b5);
+                b7 = rotlXor(b7, ROTATION_4_3, b6 += b7);
+                b9 = rotlXor(b9, ROTATION_4_4, b8 += b9);
+                b11 = rotlXor(b11, ROTATION_4_5, b10 += b11);
+                b13 = rotlXor(b13, ROTATION_4_6, b12 += b13);
+                b15 = rotlXor(b15, ROTATION_4_7, b14 += b15);
+
+                b9 = rotlXor(b9, ROTATION_5_0, b0 += b9);
+                b13 = rotlXor(b13, ROTATION_5_1, b2 += b13);
+                b11 = rotlXor(b11, ROTATION_5_2, b6 += b11);
+                b15 = rotlXor(b15, ROTATION_5_3, b4 += b15);
+                b7 = rotlXor(b7, ROTATION_5_4, b10 += b7);
+                b3 = rotlXor(b3, ROTATION_5_5, b12 += b3);
+                b5 = rotlXor(b5, ROTATION_5_6, b14 += b5);
+                b1 = rotlXor(b1, ROTATION_5_7, b8 += b1);
+
+                b7 = rotlXor(b7, ROTATION_6_0, b0 += b7);
+                b5 = rotlXor(b5, ROTATION_6_1, b2 += b5);
+                b3 = rotlXor(b3, ROTATION_6_2, b4 += b3);
+                b1 = rotlXor(b1, ROTATION_6_3, b6 += b1);
+                b15 = rotlXor(b15, ROTATION_6_4, b12 += b15);
+                b13 = rotlXor(b13, ROTATION_6_5, b14 += b13);
+                b11 = rotlXor(b11, ROTATION_6_6, b8 += b11);
+                b9 = rotlXor(b9, ROTATION_6_7, b10 += b9);
+
+                b15 = rotlXor(b15, ROTATION_7_0, b0 += b15);
+                b11 = rotlXor(b11, ROTATION_7_1, b2 += b11);
+                b13 = rotlXor(b13, ROTATION_7_2, b6 += b13);
+                b9 = rotlXor(b9, ROTATION_7_3, b4 += b9);
+                b1 = rotlXor(b1, ROTATION_7_4, b14 += b1);
+                b5 = rotlXor(b5, ROTATION_7_5, b8 += b5);
+                b3 = rotlXor(b3, ROTATION_7_6, b10 += b3);
+                b7 = rotlXor(b7, ROTATION_7_7, b12 += b7);
+
+                /*
+                 * Subkey injection for next 4 rounds.
+                 */
+                b0 += kw[dm17 + 1];
+                b1 += kw[dm17 + 2];
+                b2 += kw[dm17 + 3];
+                b3 += kw[dm17 + 4];
+                b4 += kw[dm17 + 5];
+                b5 += kw[dm17 + 6];
+                b6 += kw[dm17 + 7];
+                b7 += kw[dm17 + 8];
+                b8 += kw[dm17 + 9];
+                b9 += kw[dm17 + 10];
+                b10 += kw[dm17 + 11];
+                b11 += kw[dm17 + 12];
+                b12 += kw[dm17 + 13];
+                b13 += kw[dm17 + 14] + t[dm3 + 1];
+                b14 += kw[dm17 + 15] + t[dm3 + 2];
+                b15 += kw[dm17 + 16] + d + 1;
+
+            }
+
+            /*
+             * Output cipher state.
+             */
+            out[0] = b0;
+            out[1] = b1;
+            out[2] = b2;
+            out[3] = b3;
+            out[4] = b4;
+            out[5] = b5;
+            out[6] = b6;
+            out[7] = b7;
+            out[8] = b8;
+            out[9] = b9;
+            out[10] = b10;
+            out[11] = b11;
+            out[12] = b12;
+            out[13] = b13;
+            out[14] = b14;
+            out[15] = b15;
+        }
+
+        void decryptBlock(long[] block, long[] state)
+        {
+            final long[] kw = this.kw;
+            final long[] t = this.t;
+            final int[] mod17 = MOD17;
+            final int[] mod3 = MOD3;
+
+            /* Help the JIT avoid index bounds checks */
+            if (kw.length != 33)
+            {
+                throw new IllegalArgumentException();
+            }
+            if (t.length != 5)
+            {
+                throw new IllegalArgumentException();
+            }
+
+            long b0 = block[0];
+            long b1 = block[1];
+            long b2 = block[2];
+            long b3 = block[3];
+            long b4 = block[4];
+            long b5 = block[5];
+            long b6 = block[6];
+            long b7 = block[7];
+            long b8 = block[8];
+            long b9 = block[9];
+            long b10 = block[10];
+            long b11 = block[11];
+            long b12 = block[12];
+            long b13 = block[13];
+            long b14 = block[14];
+            long b15 = block[15];
+
+            for (int d = (ROUNDS_1024 / 4) - 1; d >= 1; d -= 2)
+            {
+                final int dm17 = mod17[d];
+                final int dm3 = mod3[d];
+
+                /* Reverse key injection for second 4 rounds */
+                b0 -= kw[dm17 + 1];
+                b1 -= kw[dm17 + 2];
+                b2 -= kw[dm17 + 3];
+                b3 -= kw[dm17 + 4];
+                b4 -= kw[dm17 + 5];
+                b5 -= kw[dm17 + 6];
+                b6 -= kw[dm17 + 7];
+                b7 -= kw[dm17 + 8];
+                b8 -= kw[dm17 + 9];
+                b9 -= kw[dm17 + 10];
+                b10 -= kw[dm17 + 11];
+                b11 -= kw[dm17 + 12];
+                b12 -= kw[dm17 + 13];
+                b13 -= kw[dm17 + 14] + t[dm3 + 1];
+                b14 -= kw[dm17 + 15] + t[dm3 + 2];
+                b15 -= kw[dm17 + 16] + d + 1;
+
+                /* Reverse second 4 mix/permute rounds */
+                b15 = xorRotr(b15, ROTATION_7_0, b0);
+                b0 -= b15;
+                b11 = xorRotr(b11, ROTATION_7_1, b2);
+                b2 -= b11;
+                b13 = xorRotr(b13, ROTATION_7_2, b6);
+                b6 -= b13;
+                b9 = xorRotr(b9, ROTATION_7_3, b4);
+                b4 -= b9;
+                b1 = xorRotr(b1, ROTATION_7_4, b14);
+                b14 -= b1;
+                b5 = xorRotr(b5, ROTATION_7_5, b8);
+                b8 -= b5;
+                b3 = xorRotr(b3, ROTATION_7_6, b10);
+                b10 -= b3;
+                b7 = xorRotr(b7, ROTATION_7_7, b12);
+                b12 -= b7;
+
+                b7 = xorRotr(b7, ROTATION_6_0, b0);
+                b0 -= b7;
+                b5 = xorRotr(b5, ROTATION_6_1, b2);
+                b2 -= b5;
+                b3 = xorRotr(b3, ROTATION_6_2, b4);
+                b4 -= b3;
+                b1 = xorRotr(b1, ROTATION_6_3, b6);
+                b6 -= b1;
+                b15 = xorRotr(b15, ROTATION_6_4, b12);
+                b12 -= b15;
+                b13 = xorRotr(b13, ROTATION_6_5, b14);
+                b14 -= b13;
+                b11 = xorRotr(b11, ROTATION_6_6, b8);
+                b8 -= b11;
+                b9 = xorRotr(b9, ROTATION_6_7, b10);
+                b10 -= b9;
+
+                b9 = xorRotr(b9, ROTATION_5_0, b0);
+                b0 -= b9;
+                b13 = xorRotr(b13, ROTATION_5_1, b2);
+                b2 -= b13;
+                b11 = xorRotr(b11, ROTATION_5_2, b6);
+                b6 -= b11;
+                b15 = xorRotr(b15, ROTATION_5_3, b4);
+                b4 -= b15;
+                b7 = xorRotr(b7, ROTATION_5_4, b10);
+                b10 -= b7;
+                b3 = xorRotr(b3, ROTATION_5_5, b12);
+                b12 -= b3;
+                b5 = xorRotr(b5, ROTATION_5_6, b14);
+                b14 -= b5;
+                b1 = xorRotr(b1, ROTATION_5_7, b8);
+                b8 -= b1;
+
+                b1 = xorRotr(b1, ROTATION_4_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_4_1, b2);
+                b2 -= b3;
+                b5 = xorRotr(b5, ROTATION_4_2, b4);
+                b4 -= b5;
+                b7 = xorRotr(b7, ROTATION_4_3, b6);
+                b6 -= b7;
+                b9 = xorRotr(b9, ROTATION_4_4, b8);
+                b8 -= b9;
+                b11 = xorRotr(b11, ROTATION_4_5, b10);
+                b10 -= b11;
+                b13 = xorRotr(b13, ROTATION_4_6, b12);
+                b12 -= b13;
+                b15 = xorRotr(b15, ROTATION_4_7, b14);
+                b14 -= b15;
+
+                /* Reverse key injection for first 4 rounds */
+                b0 -= kw[dm17];
+                b1 -= kw[dm17 + 1];
+                b2 -= kw[dm17 + 2];
+                b3 -= kw[dm17 + 3];
+                b4 -= kw[dm17 + 4];
+                b5 -= kw[dm17 + 5];
+                b6 -= kw[dm17 + 6];
+                b7 -= kw[dm17 + 7];
+                b8 -= kw[dm17 + 8];
+                b9 -= kw[dm17 + 9];
+                b10 -= kw[dm17 + 10];
+                b11 -= kw[dm17 + 11];
+                b12 -= kw[dm17 + 12];
+                b13 -= kw[dm17 + 13] + t[dm3];
+                b14 -= kw[dm17 + 14] + t[dm3 + 1];
+                b15 -= kw[dm17 + 15] + d;
+
+                /* Reverse first 4 mix/permute rounds */
+                b15 = xorRotr(b15, ROTATION_3_0, b0);
+                b0 -= b15;
+                b11 = xorRotr(b11, ROTATION_3_1, b2);
+                b2 -= b11;
+                b13 = xorRotr(b13, ROTATION_3_2, b6);
+                b6 -= b13;
+                b9 = xorRotr(b9, ROTATION_3_3, b4);
+                b4 -= b9;
+                b1 = xorRotr(b1, ROTATION_3_4, b14);
+                b14 -= b1;
+                b5 = xorRotr(b5, ROTATION_3_5, b8);
+                b8 -= b5;
+                b3 = xorRotr(b3, ROTATION_3_6, b10);
+                b10 -= b3;
+                b7 = xorRotr(b7, ROTATION_3_7, b12);
+                b12 -= b7;
+
+                b7 = xorRotr(b7, ROTATION_2_0, b0);
+                b0 -= b7;
+                b5 = xorRotr(b5, ROTATION_2_1, b2);
+                b2 -= b5;
+                b3 = xorRotr(b3, ROTATION_2_2, b4);
+                b4 -= b3;
+                b1 = xorRotr(b1, ROTATION_2_3, b6);
+                b6 -= b1;
+                b15 = xorRotr(b15, ROTATION_2_4, b12);
+                b12 -= b15;
+                b13 = xorRotr(b13, ROTATION_2_5, b14);
+                b14 -= b13;
+                b11 = xorRotr(b11, ROTATION_2_6, b8);
+                b8 -= b11;
+                b9 = xorRotr(b9, ROTATION_2_7, b10);
+                b10 -= b9;
+
+                b9 = xorRotr(b9, ROTATION_1_0, b0);
+                b0 -= b9;
+                b13 = xorRotr(b13, ROTATION_1_1, b2);
+                b2 -= b13;
+                b11 = xorRotr(b11, ROTATION_1_2, b6);
+                b6 -= b11;
+                b15 = xorRotr(b15, ROTATION_1_3, b4);
+                b4 -= b15;
+                b7 = xorRotr(b7, ROTATION_1_4, b10);
+                b10 -= b7;
+                b3 = xorRotr(b3, ROTATION_1_5, b12);
+                b12 -= b3;
+                b5 = xorRotr(b5, ROTATION_1_6, b14);
+                b14 -= b5;
+                b1 = xorRotr(b1, ROTATION_1_7, b8);
+                b8 -= b1;
+
+                b1 = xorRotr(b1, ROTATION_0_0, b0);
+                b0 -= b1;
+                b3 = xorRotr(b3, ROTATION_0_1, b2);
+                b2 -= b3;
+                b5 = xorRotr(b5, ROTATION_0_2, b4);
+                b4 -= b5;
+                b7 = xorRotr(b7, ROTATION_0_3, b6);
+                b6 -= b7;
+                b9 = xorRotr(b9, ROTATION_0_4, b8);
+                b8 -= b9;
+                b11 = xorRotr(b11, ROTATION_0_5, b10);
+                b10 -= b11;
+                b13 = xorRotr(b13, ROTATION_0_6, b12);
+                b12 -= b13;
+                b15 = xorRotr(b15, ROTATION_0_7, b14);
+                b14 -= b15;
+            }
+
+            /*
+             * First subkey uninjection.
+             */
+            b0 -= kw[0];
+            b1 -= kw[1];
+            b2 -= kw[2];
+            b3 -= kw[3];
+            b4 -= kw[4];
+            b5 -= kw[5];
+            b6 -= kw[6];
+            b7 -= kw[7];
+            b8 -= kw[8];
+            b9 -= kw[9];
+            b10 -= kw[10];
+            b11 -= kw[11];
+            b12 -= kw[12];
+            b13 -= kw[13] + t[0];
+            b14 -= kw[14] + t[1];
+            b15 -= kw[15];
+
+            /*
+             * Output cipher state.
+             */
+            state[0] = b0;
+            state[1] = b1;
+            state[2] = b2;
+            state[3] = b3;
+            state[4] = b4;
+            state[5] = b5;
+            state[6] = b6;
+            state[7] = b7;
+            state[8] = b8;
+            state[9] = b9;
+            state[10] = b10;
+            state[11] = b11;
+            state[12] = b12;
+            state[13] = b13;
+            state[14] = b14;
+            state[15] = b15;
+        }
+
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
index 0703fd6..f16f6d4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/VMPCEngine.java
@@ -44,7 +44,6 @@
         }
 
         ParametersWithIV ivParams = (ParametersWithIV) params;
-        KeyParameter key = (KeyParameter) ivParams.getParameters();
 
         if (!(ivParams.getParameters() instanceof KeyParameter))
         {
@@ -52,6 +51,8 @@
                 "VMPC init parameters must include a key");
         }
 
+        KeyParameter key = (KeyParameter) ivParams.getParameters();
+
         this.workingIV = ivParams.getIV();
 
         if (workingIV == null || workingIV.length < 1 || workingIV.length > 768)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
new file mode 100644
index 0000000..5b2181d
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XSalsa20Engine.java
@@ -0,0 +1,65 @@
+package org.bouncycastle.crypto.engines;
+
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Implementation of Daniel J. Bernstein's XSalsa20 stream cipher - Salsa20 with an extended nonce.
+ * <p>
+ * XSalsa20 requires a 256 bit key, and a 192 bit nonce.
+ */
+public class XSalsa20Engine extends Salsa20Engine
+{
+
+    public String getAlgorithmName()
+    {
+        return "XSalsa20";
+    }
+
+    protected int getNonceSize()
+    {
+        return 24;
+    }
+
+    /**
+     * XSalsa20 key generation: process 256 bit input key and 128 bits of the input nonce
+     * using a core Salsa20 function without input addition to produce 256 bit working key
+     * and use that with the remaining 64 bits of nonce to initialize a standard Salsa20 engine state.
+     */
+    protected void setKey(byte[] keyBytes, byte[] ivBytes)
+    {
+        if (keyBytes.length != 32)
+        {
+            throw new IllegalArgumentException(getAlgorithmName() + " requires a 256 bit key");
+        }
+
+        // Set key for HSalsa20
+        super.setKey(keyBytes, ivBytes);
+
+        // Pack next 64 bits of IV into engine state instead of counter
+        engineState[8] = Pack.littleEndianToInt(ivBytes, 8);
+        engineState[9] = Pack.littleEndianToInt(ivBytes, 12);
+
+        // Process engine state to generate Salsa20 key
+        int[] hsalsa20Out = new int[engineState.length];
+        salsaCore(20, engineState, hsalsa20Out);
+
+        // Set new key, removing addition in last round of salsaCore
+        engineState[1] = hsalsa20Out[0] - engineState[0];
+        engineState[2] = hsalsa20Out[5] - engineState[5];
+        engineState[3] = hsalsa20Out[10] - engineState[10];
+        engineState[4] = hsalsa20Out[15] - engineState[15];
+
+        engineState[11] = hsalsa20Out[6] - engineState[6];
+        engineState[12] = hsalsa20Out[7] - engineState[7];
+        engineState[13] = hsalsa20Out[8] - engineState[8];
+        engineState[14] = hsalsa20Out[9] - engineState[9];
+
+        // Last 64 bits of input IV
+        engineState[6] = Pack.littleEndianToInt(ivBytes, 16);
+        engineState[7] = Pack.littleEndianToInt(ivBytes, 20);
+
+        // Counter reset
+        resetCounter();
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
index f037da4..fb21bbc 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/engines/XTEAEngine.java
@@ -107,6 +107,11 @@
     private void setKey(
         byte[]      key)
     {
+        if (key.length != 16) 
+        {
+            throw new IllegalArgumentException("Key size must be 128 bits.");
+        }
+
         int i, j;
         for (i = j = 0; i < 4; i++,j+=4)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html
deleted file mode 100644
index e945dac..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/engines/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic cipher classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html
deleted file mode 100644
index 390a540..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/examples/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Simple examples of light weight API usage.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
index 2ef8dd2..16c8b91 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/BaseKDFBytesGenerator.java
@@ -1,9 +1,9 @@
 package org.bouncycastle.crypto.generators;
 
 import org.bouncycastle.crypto.DataLengthException;
-import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.DerivationParameters;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.DigestDerivationFunction;
 import org.bouncycastle.crypto.params.ISO18033KDFParameters;
 import org.bouncycastle.crypto.params.KDFParameters;
 import org.bouncycastle.crypto.util.Pack;
@@ -13,7 +13,8 @@
  * 18033 <br>
  * This implementation is based on ISO 18033/P1363a.
  */
-public class BaseKDFBytesGenerator implements DerivationFunction
+public class BaseKDFBytesGenerator
+    implements DigestDerivationFunction
 {
     private int    counterStart;
     private Digest digest;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
index 749b0cc..f7a3df2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/DSAParametersGenerator.java
@@ -226,10 +226,10 @@
         int seedlen = N;
         byte[] seed = new byte[seedlen / 8];
 
-// 3. n = ceiling(L ⁄ outlen) – 1.
+// 3. n = ceiling(L / outlen) - 1.
         int n = (L - 1) / outlen;
 
-// 4. b = L – 1 – (n ∗ outlen).
+// 4. b = L - 1 - (n * outlen).
         int b = (L - 1) % outlen;
 
         byte[] output = new byte[d.getDigestSize()];
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
index d77bd74..d5f5fc8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/ECKeyPairGenerator.java
@@ -26,6 +26,11 @@
 
         this.random = ecP.getRandom();
         this.params = ecP.getDomainParameters();
+
+        if (this.random == null)
+        {
+            this.random = new SecureRandom();
+        }
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
new file mode 100644
index 0000000..306530e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFCounterBytesGenerator.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.MacDerivationFunction;
+import org.bouncycastle.crypto.params.KDFCounterParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFCounterBytesGenerator
+    implements MacDerivationFunction
+{
+
+    private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+
+    // please refer to the standard for the meaning of the variable names
+    // all field lengths are in bytes, not in bits as specified by the standard
+
+    // fields set by the constructor
+    private final Mac prf;
+    private final int h;
+
+    // fields set by init
+    private byte[] fixedInputData;
+    private int maxSizeExcl;
+    // ios is i defined as an octet string (the binary representation)
+    private byte[] ios;
+
+    // operational
+    private int generatedBytes;
+    // k is used as buffer for all K(i) values
+    private byte[] k;
+
+
+    public KDFCounterBytesGenerator(Mac prf)
+    {
+        this.prf = prf;
+        this.h = prf.getMacSize();
+        this.k = new byte[h];
+    }
+
+
+    public void init(DerivationParameters param)
+    {
+        if (!(param instanceof KDFCounterParameters))
+        {
+            throw new IllegalArgumentException("Wrong type of arguments given");
+        }
+
+        KDFCounterParameters kdfParams = (KDFCounterParameters)param;
+
+        // --- init mac based PRF ---
+
+        this.prf.init(new KeyParameter(kdfParams.getKI()));
+
+        // --- set arguments ---
+
+        this.fixedInputData = kdfParams.getFixedInputData();
+
+        int r = kdfParams.getR();
+        this.ios = new byte[r / 8];
+
+        BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+        this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+            Integer.MAX_VALUE : maxSize.intValue();
+
+        // --- set operational state ---
+
+        generatedBytes = 0;
+    }
+
+
+    public Mac getMac()
+    {
+        return prf;
+    }
+
+    public int generateBytes(byte[] out, int outOff, int len)
+        throws DataLengthException, IllegalArgumentException
+    {
+
+        int generatedBytesAfter = generatedBytes + len;
+        if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+        {
+            throw new DataLengthException(
+                "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+        }
+
+        if (generatedBytes % h == 0)
+        {
+            generateNext();
+        }
+
+        // copy what is left in the currentT (1..hash
+        int toGenerate = len;
+        int posInK = generatedBytes % h;
+        int leftInK = h - generatedBytes % h;
+        int toCopy = Math.min(leftInK, toGenerate);
+        System.arraycopy(k, posInK, out, outOff, toCopy);
+        generatedBytes += toCopy;
+        toGenerate -= toCopy;
+        outOff += toCopy;
+
+        while (toGenerate > 0)
+        {
+            generateNext();
+            toCopy = Math.min(h, toGenerate);
+            System.arraycopy(k, 0, out, outOff, toCopy);
+            generatedBytes += toCopy;
+            toGenerate -= toCopy;
+            outOff += toCopy;
+        }
+
+        return len;
+    }
+
+    private void generateNext()
+    {
+        int i = generatedBytes / h + 1;
+
+        // encode i into counter buffer
+        switch (ios.length)
+        {
+        case 4:
+            ios[0] = (byte)(i >>> 24);
+            // fall through
+        case 3:
+            ios[ios.length - 3] = (byte)(i >>> 16);
+            // fall through
+        case 2:
+            ios[ios.length - 2] = (byte)(i >>> 8);
+            // fall through
+        case 1:
+            ios[ios.length - 1] = (byte)i;
+            break;
+        default:
+            throw new IllegalStateException("Unsupported size of counter i");
+        }
+
+
+        // special case for K(0): K(0) is empty, so no update
+        prf.update(ios, 0, ios.length);
+        prf.update(fixedInputData, 0, fixedInputData.length);
+        prf.doFinal(k, 0);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java
new file mode 100644
index 0000000..6115a1a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFDoublePipelineIterationBytesGenerator.java
@@ -0,0 +1,181 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.MacDerivationFunction;
+import org.bouncycastle.crypto.params.KDFDoublePipelineIterationParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFDoublePipelineIterationBytesGenerator
+    implements MacDerivationFunction
+{
+
+    private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+
+    // please refer to the standard for the meaning of the variable names
+    // all field lengths are in bytes, not in bits as specified by the standard
+
+    // fields set by the constructor
+    private final Mac prf;
+    private final int h;
+
+    // fields set by init
+    private byte[] fixedInputData;
+    private int maxSizeExcl;
+    // ios is i defined as an octet string (the binary representation)
+    private byte[] ios;
+    private boolean useCounter;
+
+    // operational
+    private int generatedBytes;
+    // k is used as buffer for all K(i) values
+    private byte[] a;
+    private byte[] k;
+
+
+    public KDFDoublePipelineIterationBytesGenerator(Mac prf)
+    {
+        this.prf = prf;
+        this.h = prf.getMacSize();
+        this.a = new byte[h];
+        this.k = new byte[h];
+    }
+
+    public void init(DerivationParameters params)
+    {
+        if (!(params instanceof KDFDoublePipelineIterationParameters))
+        {
+            throw new IllegalArgumentException("Wrong type of arguments given");
+        }
+
+        KDFDoublePipelineIterationParameters dpiParams = (KDFDoublePipelineIterationParameters)params;
+
+        // --- init mac based PRF ---
+
+        this.prf.init(new KeyParameter(dpiParams.getKI()));
+
+        // --- set arguments ---
+
+        this.fixedInputData = dpiParams.getFixedInputData();
+
+        int r = dpiParams.getR();
+        this.ios = new byte[r / 8];
+
+        if (dpiParams.useCounter())
+        {
+            // this is more conservative than the spec
+            BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+            this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+                Integer.MAX_VALUE : maxSize.intValue();
+        }
+        else
+        {
+            this.maxSizeExcl = Integer.MAX_VALUE;
+        }
+
+        this.useCounter = dpiParams.useCounter();
+
+        // --- set operational state ---
+
+        generatedBytes = 0;
+    }
+
+    public Mac getMac()
+    {
+        return prf;
+    }
+
+    public int generateBytes(byte[] out, int outOff, int len)
+        throws DataLengthException, IllegalArgumentException
+    {
+
+        int generatedBytesAfter = generatedBytes + len;
+        if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+        {
+            throw new DataLengthException(
+                "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+        }
+
+        if (generatedBytes % h == 0)
+        {
+            generateNext();
+        }
+
+        // copy what is left in the currentT (1..hash
+        int toGenerate = len;
+        int posInK = generatedBytes % h;
+        int leftInK = h - generatedBytes % h;
+        int toCopy = Math.min(leftInK, toGenerate);
+        System.arraycopy(k, posInK, out, outOff, toCopy);
+        generatedBytes += toCopy;
+        toGenerate -= toCopy;
+        outOff += toCopy;
+
+        while (toGenerate > 0)
+        {
+            generateNext();
+            toCopy = Math.min(h, toGenerate);
+            System.arraycopy(k, 0, out, outOff, toCopy);
+            generatedBytes += toCopy;
+            toGenerate -= toCopy;
+            outOff += toCopy;
+        }
+
+        return len;
+    }
+
+    private void generateNext()
+    {
+
+        if (generatedBytes == 0)
+        {
+            // --- step 4 ---
+            prf.update(fixedInputData, 0, fixedInputData.length);
+            prf.doFinal(a, 0);
+        }
+        else
+        {
+            // --- step 5a ---
+            prf.update(a, 0, a.length);
+            prf.doFinal(a, 0);
+        }
+
+        // --- step 5b ---
+        prf.update(a, 0, a.length);
+
+        if (useCounter)
+        {
+            int i = generatedBytes / h + 1;
+
+            // encode i into counter buffer
+            switch (ios.length)
+            {
+            case 4:
+                ios[0] = (byte)(i >>> 24);
+                // fall through
+            case 3:
+                ios[ios.length - 3] = (byte)(i >>> 16);
+                // fall through
+            case 2:
+                ios[ios.length - 2] = (byte)(i >>> 8);
+                // fall through
+            case 1:
+                ios[ios.length - 1] = (byte)i;
+                break;
+            default:
+                throw new IllegalStateException("Unsupported size of counter i");
+            }
+            prf.update(ios, 0, ios.length);
+        }
+
+        prf.update(fixedInputData, 0, fixedInputData.length);
+        prf.doFinal(k, 0);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java
new file mode 100644
index 0000000..6003037
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/KDFFeedbackBytesGenerator.java
@@ -0,0 +1,175 @@
+package org.bouncycastle.crypto.generators;
+
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.MacDerivationFunction;
+import org.bouncycastle.crypto.params.KDFFeedbackParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+
+/**
+ * This KDF has been defined by the publicly available NIST SP 800-108 specification.
+ */
+public class KDFFeedbackBytesGenerator
+    implements MacDerivationFunction
+{
+
+    private static final BigInteger INTEGER_MAX = BigInteger.valueOf(Integer.MAX_VALUE);
+    private static final BigInteger TWO = BigInteger.valueOf(2);
+
+    // please refer to the standard for the meaning of the variable names
+    // all field lengths are in bytes, not in bits as specified by the standard
+
+    // fields set by the constructor
+    private final Mac prf;
+    private final int h;
+
+    // fields set by init
+    private byte[] fixedInputData;
+    private int maxSizeExcl;
+    // ios is i defined as an octet string (the binary representation)
+    private byte[] ios;
+    private byte[] iv;
+    private boolean useCounter;
+
+    // operational
+    private int generatedBytes;
+    // k is used as buffer for all K(i) values
+    private byte[] k;
+
+
+    public KDFFeedbackBytesGenerator(Mac prf)
+    {
+        this.prf = prf;
+        this.h = prf.getMacSize();
+        this.k = new byte[h];
+    }
+
+    public void init(DerivationParameters params)
+    {
+        if (!(params instanceof KDFFeedbackParameters))
+        {
+            throw new IllegalArgumentException("Wrong type of arguments given");
+        }
+
+        KDFFeedbackParameters feedbackParams = (KDFFeedbackParameters)params;
+
+        // --- init mac based PRF ---
+
+        this.prf.init(new KeyParameter(feedbackParams.getKI()));
+
+        // --- set arguments ---
+
+        this.fixedInputData = feedbackParams.getFixedInputData();
+
+        int r = feedbackParams.getR();
+        this.ios = new byte[r / 8];
+
+        if (feedbackParams.useCounter())
+        {
+            // this is more conservative than the spec
+            BigInteger maxSize = TWO.pow(r).multiply(BigInteger.valueOf(h));
+            this.maxSizeExcl = maxSize.compareTo(INTEGER_MAX) == 1 ?
+                Integer.MAX_VALUE : maxSize.intValue();
+        }
+        else
+        {
+            this.maxSizeExcl = Integer.MAX_VALUE;
+        }
+
+        this.iv = feedbackParams.getIV();
+        this.useCounter = feedbackParams.useCounter();
+
+        // --- set operational state ---
+
+        generatedBytes = 0;
+    }
+
+    public Mac getMac()
+    {
+        return prf;
+    }
+
+    public int generateBytes(byte[] out, int outOff, int len)
+        throws DataLengthException, IllegalArgumentException
+    {
+
+        int generatedBytesAfter = generatedBytes + len;
+        if (generatedBytesAfter < 0 || generatedBytesAfter >= maxSizeExcl)
+        {
+            throw new DataLengthException(
+                "Current KDFCTR may only be used for " + maxSizeExcl + " bytes");
+        }
+
+        if (generatedBytes % h == 0)
+        {
+            generateNext();
+        }
+
+        // copy what is left in the currentT (1..hash
+        int toGenerate = len;
+        int posInK = generatedBytes % h;
+        int leftInK = h - generatedBytes % h;
+        int toCopy = Math.min(leftInK, toGenerate);
+        System.arraycopy(k, posInK, out, outOff, toCopy);
+        generatedBytes += toCopy;
+        toGenerate -= toCopy;
+        outOff += toCopy;
+
+        while (toGenerate > 0)
+        {
+            generateNext();
+            toCopy = Math.min(h, toGenerate);
+            System.arraycopy(k, 0, out, outOff, toCopy);
+            generatedBytes += toCopy;
+            toGenerate -= toCopy;
+            outOff += toCopy;
+        }
+
+        return len;
+    }
+
+    private void generateNext()
+    {
+
+        // TODO enable IV
+        if (generatedBytes == 0)
+        {
+            prf.update(iv, 0, iv.length);
+        }
+        else
+        {
+            prf.update(k, 0, k.length);
+        }
+
+        if (useCounter)
+        {
+            int i = generatedBytes / h + 1;
+
+            // encode i into counter buffer
+            switch (ios.length)
+            {
+            case 4:
+                ios[0] = (byte)(i >>> 24);
+                // fall through
+            case 3:
+                ios[ios.length - 3] = (byte)(i >>> 16);
+                // fall through
+            case 2:
+                ios[ios.length - 2] = (byte)(i >>> 8);
+                // fall through
+            case 1:
+                ios[ios.length - 1] = (byte)i;
+                break;
+            default:
+                throw new IllegalStateException("Unsupported size of counter i");
+            }
+            prf.update(ios, 0, ios.length);
+        }
+
+        prf.update(fixedInputData, 0, fixedInputData.length);
+        prf.doFinal(k, 0);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
index 640ead4..0954d48 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/PKCS5S2ParametersGenerator.java
@@ -58,7 +58,7 @@
         hMac.doFinal(state, 0);
 
         System.arraycopy(state, 0, out, outOff, state.length);
-        
+
         for (int count = 1; count < c; count++)
         {
             hMac.update(state, 0, state.length);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
new file mode 100644
index 0000000..9165973
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/generators/Poly1305KeyGenerator.java
@@ -0,0 +1,117 @@
+package org.bouncycastle.crypto.generators;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.KeyGenerationParameters;
+import org.bouncycastle.crypto.macs.Poly1305;
+
+/**
+ * Generates keys for the Poly1305 MAC.
+ * <p>
+ * Poly1305 keys are 256 bit keys consisting of a 128 bit secret key used for the underlying block
+ * cipher followed by a 128 bit {@code r} value used for the polynomial portion of the Mac. <br>
+ * The {@code r} value has a specific format with some bits required to be cleared, resulting in an
+ * effective 106 bit key. <br>
+ * A separately generated 256 bit key can be modified to fit the Poly1305 key format by using the
+ * {@link #clamp(byte[])} method to clear the required bits.
+ *
+ * @see Poly1305
+ */
+public class Poly1305KeyGenerator
+    extends CipherKeyGenerator
+{
+    private static final byte R_MASK_LOW_2 = (byte)0xFC;
+    private static final byte R_MASK_HIGH_4 = (byte)0x0F;
+
+    /**
+     * Initialises the key generator.<br>
+     * Poly1305 keys are always 256 bits, so the key length in the provided parameters is ignored.
+     */
+    public void init(KeyGenerationParameters param)
+    {
+        // Poly1305 keys are always 256 bits
+        super.init(new KeyGenerationParameters(param.getRandom(), 256));
+    }
+
+    /**
+     * Generates a 256 bit key in the format required for Poly1305 - e.g.
+     * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
+     * as per {@link #clamp(byte[])}.
+     */
+    public byte[] generateKey()
+    {
+        final byte[] key = super.generateKey();
+        clamp(key);
+        return key;
+    }
+
+    /**
+     * Modifies an existing 32 byte key value to comply with the requirements of the Poly1305 key by
+     * clearing required bits in the <code>r</code> (second 16 bytes) portion of the key.<br>
+     * Specifically:
+     * <ul>
+     * <li>r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})</li>
+     * <li>r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252})</li>
+     * </ul>
+     *
+     * @param a 32 byte key value <code>k[0] ... k[15], r[0] ... r[15]</code>
+     */
+    public static void clamp(byte[] key)
+    {
+        /*
+         * Key is k[0] ... k[15], r[0] ... r[15] as per poly1305_aes_clamp in ref impl.
+         */
+        if (key.length != 32)
+        {
+            throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
+        }
+
+        /*
+         * r[3], r[7], r[11], r[15] have top four bits clear (i.e., are {0, 1, . . . , 15})
+         */
+        key[19] &= R_MASK_HIGH_4;
+        key[23] &= R_MASK_HIGH_4;
+        key[27] &= R_MASK_HIGH_4;
+        key[31] &= R_MASK_HIGH_4;
+
+        /*
+         * r[4], r[8], r[12] have bottom two bits clear (i.e., are in {0, 4, 8, . . . , 252}).
+         */
+        key[20] &= R_MASK_LOW_2;
+        key[24] &= R_MASK_LOW_2;
+        key[28] &= R_MASK_LOW_2;
+    }
+
+    /**
+     * Checks a 32 byte key for compliance with the Poly1305 key requirements, e.g.
+     * <code>k[0] ... k[15], r[0] ... r[15]</code> with the required bits in <code>r</code> cleared
+     * as per {@link #clamp(byte[])}.
+     *
+     * @throws IllegalArgumentException if the key is of the wrong length, or has invalid bits set
+     *             in the <code>r</code> portion of the key.
+     */
+    public static void checkKey(byte[] key)
+    {
+        if (key.length != 32)
+        {
+            throw new IllegalArgumentException("Poly1305 key must be 256 bits.");
+        }
+
+        checkMask(key[19], R_MASK_HIGH_4);
+        checkMask(key[23], R_MASK_HIGH_4);
+        checkMask(key[27], R_MASK_HIGH_4);
+        checkMask(key[31], R_MASK_HIGH_4);
+
+        checkMask(key[20], R_MASK_LOW_2);
+        checkMask(key[24], R_MASK_LOW_2);
+        checkMask(key[28], R_MASK_LOW_2);
+    }
+
+    private static void checkMask(byte b, byte mask)
+    {
+        if ((b & (~mask)) != 0)
+        {
+            throw new IllegalArgumentException("Invalid format for r portion of Poly1305 key.");
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html
deleted file mode 100644
index 9d73ce3..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/generators/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Generators for keys, key pairs and password based encryption algorithms.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
index bb09a76..93b04e9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherInputStream.java
@@ -5,15 +5,15 @@
 import java.io.InputStream;
 
 import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
 
 /**
- * A CipherInputStream is composed of an InputStream and a BufferedBlockCipher so
- * that read() methods return data that are read in from the
- * underlying InputStream but have been additionally processed by the
- * Cipher.  The Cipher must be fully initialized before being used by
- * a CipherInputStream.
- * <p>
+ * A CipherInputStream is composed of an InputStream and a cipher so that read() methods return data
+ * that are read in from the underlying InputStream but have been additionally processed by the
+ * Cipher. The cipher must be fully initialized before being used by a CipherInputStream.
+ * <p/>
  * For example, if the Cipher is initialized for decryption, the
  * CipherInputStream will attempt to read in data and decrypt them,
  * before returning the decrypted data.
@@ -23,9 +23,10 @@
 {
     private BufferedBlockCipher bufferedBlockCipher;
     private StreamCipher streamCipher;
+    private AEADBlockCipher aeadBlockCipher;
 
-    private byte[] buf;
-    private byte[] inBuf;
+    private final byte[] buf;
+    private final byte[] inBuf;
 
     private int bufOff;
     private int maxBuf;
@@ -62,95 +63,116 @@
     }
 
     /**
-     * grab the next chunk of input from the underlying input stream
+     * Constructs a CipherInputStream from an InputStream and an AEADBlockCipher.
+     */
+    public CipherInputStream(InputStream is, AEADBlockCipher cipher)
+    {
+        super(is);
+
+        this.aeadBlockCipher = cipher;
+
+        buf = new byte[cipher.getOutputSize(INPUT_BUF_SIZE)];
+        inBuf = new byte[INPUT_BUF_SIZE];
+    }
+
+    /**
+     * Read data from underlying stream and process with cipher until end of stream or some data is
+     * available after cipher processing.
+     *
+     * @return -1 to indicate end of stream, or the number of bytes (> 0) available.
      */
     private int nextChunk()
         throws IOException
     {
-        int available = super.available();
-
-        // must always try to read 1 byte!
-        // some buggy InputStreams return < 0!
-        if (available <= 0)
+        if (finalized)
         {
-            available = 1;
+            return -1;
         }
 
-        if (available > inBuf.length)
-        {
-            available = super.read(inBuf, 0, inBuf.length);
-        }
-        else
-        {
-            available = super.read(inBuf, 0, available);
-        }
+        bufOff = 0;
+        maxBuf = 0;
 
-        if (available < 0)
+        // Keep reading until EOF or cipher processing produces data
+        while (maxBuf == 0)
         {
-            if (finalized)
+            int read = in.read(inBuf);
+            if (read == -1)
             {
-                return -1;
+                finaliseCipher();
+                if (maxBuf == 0)
+                {
+                    return -1;
+                }
+                return maxBuf;
             }
 
             try
             {
                 if (bufferedBlockCipher != null)
                 {
-                    maxBuf = bufferedBlockCipher.doFinal(buf, 0);
+                    maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, read, buf, 0);
+                }
+                else if (aeadBlockCipher != null)
+                {
+                    maxBuf = aeadBlockCipher.processBytes(inBuf, 0, read, buf, 0);
                 }
                 else
                 {
-                    maxBuf = 0; // a stream cipher
+                    streamCipher.processBytes(inBuf, 0, read, buf, 0);
+                    maxBuf = read;
                 }
             }
             catch (Exception e)
             {
-                throw new IOException("error processing stream: " + e.toString());
-            }
-
-            bufOff = 0;
-
-            finalized = true;
-
-            if (bufOff == maxBuf)
-            {
-                return -1;
+                throw new IOException("Error processing stream " + e);
             }
         }
-        else
-        {
-            bufOff = 0;
-
-            try
-            {
-                if (bufferedBlockCipher != null)
-                {
-                    maxBuf = bufferedBlockCipher.processBytes(inBuf, 0, available, buf, 0);
-                }
-                else
-                {
-                    streamCipher.processBytes(inBuf, 0, available, buf, 0);
-                    maxBuf = available;
-                }
-            }
-            catch (Exception e)
-            {
-                throw new IOException("error processing stream: " + e.toString());
-            }
-
-            if (maxBuf == 0)    // not enough bytes read for first block...
-            {
-                return nextChunk();
-            }
-        }
-
         return maxBuf;
     }
 
+    private void finaliseCipher()
+        throws IOException
+    {
+        try
+        {
+            finalized = true;
+            if (bufferedBlockCipher != null)
+            {
+                maxBuf = bufferedBlockCipher.doFinal(buf, 0);
+            }
+            else if (aeadBlockCipher != null)
+            {
+                maxBuf = aeadBlockCipher.doFinal(buf, 0);
+            }
+            else
+            {
+                maxBuf = 0; // a stream cipher
+            }
+        }
+        catch (final InvalidCipherTextException e)
+        {
+            throw new InvalidCipherTextIOException("Error finalising cipher", e);
+        }
+        catch (Exception e)
+        {
+            throw new IOException("Error finalising cipher " + e);
+        }
+    }
+
+    /**
+     * Reads data from the underlying stream and processes it with the cipher until the cipher
+     * outputs data, and returns the next available byte.
+     * <p/>
+     * If the underlying stream is exhausted by this call, the cipher will be finalised.
+     *
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
     public int read()
         throws IOException
     {
-        if (bufOff == maxBuf)
+        if (bufOff >= maxBuf)
         {
             if (nextChunk() < 0)
             {
@@ -161,6 +183,19 @@
         return buf[bufOff++] & 0xff;
     }
 
+    /**
+     * Reads data from the underlying stream and processes it with the cipher until the cipher
+     * outputs data, and then returns up to <code>b.length</code> bytes in the provided array.
+     * <p/>
+     * If the underlying stream is exhausted by this call, the cipher will be finalised.
+     *
+     * @param b the buffer into which the data is read.
+     * @return the total number of bytes read into the buffer, or <code>-1</code> if there is no
+     *         more data because the end of the stream has been reached.
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
     public int read(
         byte[] b)
         throws IOException
@@ -168,13 +203,28 @@
         return read(b, 0, b.length);
     }
 
+    /**
+     * Reads data from the underlying stream and processes it with the cipher until the cipher
+     * outputs data, and then returns up to <code>len</code> bytes in the provided array.
+     * <p/>
+     * If the underlying stream is exhausted by this call, the cipher will be finalised.
+     *
+     * @param b   the buffer into which the data is read.
+     * @param off the start offset in the destination array <code>b</code>
+     * @param len the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or <code>-1</code> if there is no
+     *         more data because the end of the stream has been reached.
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
     public int read(
         byte[] b,
         int off,
         int len)
         throws IOException
     {
-        if (bufOff == maxBuf)
+        if (bufOff >= maxBuf)
         {
             if (nextChunk() < 0)
             {
@@ -182,22 +232,10 @@
             }
         }
 
-        int available = maxBuf - bufOff;
-
-        if (len > available)
-        {
-            System.arraycopy(buf, bufOff, b, off, available);
-            bufOff = maxBuf;
-
-            return available;
-        }
-        else
-        {
-            System.arraycopy(buf, bufOff, b, off, len);
-            bufOff += len;
-
-            return len;
-        }
+        int toSupply = Math.min(len, available());
+        System.arraycopy(buf, bufOff, b, off, toSupply);
+        bufOff += toSupply;
+        return toSupply;
     }
 
     public long skip(
@@ -209,20 +247,9 @@
             return 0;
         }
 
-        int available = maxBuf - bufOff;
-
-        if (n > available)
-        {
-            bufOff = maxBuf;
-
-            return available;
-        }
-        else
-        {
-            bufOff += (int)n;
-
-            return (int)n;
-        }
+        int skip = (int)Math.min(n, available());
+        bufOff += skip;
+        return skip;
     }
 
     public int available()
@@ -231,14 +258,44 @@
         return maxBuf - bufOff;
     }
 
+    /**
+     * Closes the underlying input stream and finalises the processing of the data by the cipher.
+     *
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
     public void close()
         throws IOException
     {
-        super.close();
+        try
+        {
+            in.close();
+        }
+        finally
+        {
+            if (!finalized)
+            {
+                // Reset the cipher, discarding any data buffered in it
+                // Errors in cipher finalisation trump I/O error closing input
+                finaliseCipher();
+            }
+        }
+        maxBuf = bufOff = 0;
+    }
+
+    public void mark(int readlimit)
+    {
+    }
+
+    public void reset()
+        throws IOException
+    {
     }
 
     public boolean markSupported()
     {
         return false;
     }
+
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
index 17a7b6d..9beb5b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/CipherOutputStream.java
@@ -5,15 +5,27 @@
 import java.io.OutputStream;
 
 import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.InvalidCipherTextException;
 import org.bouncycastle.crypto.StreamCipher;
+import org.bouncycastle.crypto.modes.AEADBlockCipher;
 
+/**
+ * A CipherOutputStream is composed of an OutputStream and a cipher so that write() methods process
+ * the written data with the cipher, and the output of the cipher is in turn written to the
+ * underlying OutputStream. The cipher must be fully initialized before being used by a
+ * CipherInputStream.
+ * <p/>
+ * For example, if the cipher is initialized for encryption, the CipherOutputStream will encrypt the
+ * data before writing the encrypted data to the underlying stream.
+ */
 public class CipherOutputStream
     extends FilterOutputStream
 {
     private BufferedBlockCipher bufferedBlockCipher;
     private StreamCipher streamCipher;
+    private AEADBlockCipher aeadBlockCipher;
 
-    private byte[] oneByte = new byte[1];
+    private final byte[] oneByte = new byte[1];
     private byte[] buf;
 
     /**
@@ -26,7 +38,6 @@
     {
         super(os);
         this.bufferedBlockCipher = cipher;
-        this.buf = new byte[cipher.getBlockSize()];
     }
 
     /**
@@ -42,10 +53,19 @@
     }
 
     /**
+     * Constructs a CipherOutputStream from an OutputStream and a AEADBlockCipher.
+     */
+    public CipherOutputStream(OutputStream os, AEADBlockCipher cipher)
+    {
+        super(os);
+        this.aeadBlockCipher = cipher;
+    }
+
+    /**
      * Writes the specified byte to this output stream.
      *
      * @param b the <code>byte</code>.
-     * @exception java.io.IOException if an I/O error occurs.
+     * @throws java.io.IOException if an I/O error occurs.
      */
     public void write(
         int b)
@@ -53,32 +73,27 @@
     {
         oneByte[0] = (byte)b;
 
-        if (bufferedBlockCipher != null)
+        if (streamCipher != null)
         {
-            int len = bufferedBlockCipher.processBytes(oneByte, 0, 1, buf, 0);
-
-            if (len != 0)
-            {
-                out.write(buf, 0, len);
-            }
+            out.write(streamCipher.returnByte((byte)b));
         }
         else
         {
-            out.write(streamCipher.returnByte((byte)b));
+            write(oneByte, 0, 1);
         }
     }
 
     /**
      * Writes <code>b.length</code> bytes from the specified byte array
      * to this output stream.
-     * <p>
+     * <p/>
      * The <code>write</code> method of
      * <code>CipherOutputStream</code> calls the <code>write</code>
      * method of three arguments with the three arguments
      * <code>b</code>, <code>0</code>, and <code>b.length</code>.
      *
      * @param b the data.
-     * @exception java.io.IOException if an I/O error occurs.
+     * @throws java.io.IOException if an I/O error occurs.
      * @see #write(byte[], int, int)
      */
     public void write(
@@ -92,10 +107,10 @@
      * Writes <code>len</code> bytes from the specified byte array
      * starting at offset <code>off</code> to this output stream.
      *
-     * @param b the data.
+     * @param b   the data.
      * @param off the start offset in the data.
      * @param len the number of bytes to write.
-     * @exception java.io.IOException if an I/O error occurs.
+     * @throws java.io.IOException if an I/O error occurs.
      */
     public void write(
         byte[] b,
@@ -103,10 +118,10 @@
         int len)
         throws IOException
     {
+        ensureCapacity(len);
+
         if (bufferedBlockCipher != null)
         {
-            byte[] buf = new byte[bufferedBlockCipher.getOutputSize(len)];
-
             int outLen = bufferedBlockCipher.processBytes(b, off, len, buf, 0);
 
             if (outLen != 0)
@@ -114,10 +129,17 @@
                 out.write(buf, 0, outLen);
             }
         }
+        else if (aeadBlockCipher != null)
+        {
+            int outLen = aeadBlockCipher.processBytes(b, off, len, buf, 0);
+
+            if (outLen != 0)
+            {
+                out.write(buf, 0, outLen);
+            }
+        }
         else
         {
-            byte[] buf = new byte[len];
-
             streamCipher.processBytes(b, off, len, buf, 0);
 
             out.write(buf, 0, len);
@@ -125,49 +147,78 @@
     }
 
     /**
+     * Ensure the ciphertext buffer has space sufficient to accept an upcoming output.
+     *
+     * @param outputSize the size of the pending update.
+     */
+    private void ensureCapacity(int outputSize)
+    {
+        // This overestimates buffer on updates for AEAD/padded, but keeps it simple.
+        int bufLen;
+        if (bufferedBlockCipher != null)
+        {
+            bufLen = bufferedBlockCipher.getOutputSize(outputSize);
+        }
+        else if (aeadBlockCipher != null)
+        {
+            bufLen = aeadBlockCipher.getOutputSize(outputSize);
+        }
+        else
+        {
+            bufLen = outputSize;
+        }
+        if ((buf == null) || (buf.length < bufLen))
+        {
+            buf = new byte[bufLen];
+        }
+    }
+
+    /**
      * Flushes this output stream by forcing any buffered output bytes
      * that have already been processed by the encapsulated cipher object
      * to be written out.
-     *
-     * <p>
+     * <p/>
+     * <p/>
      * Any bytes buffered by the encapsulated cipher
      * and waiting to be processed by it will not be written out. For example,
      * if the encapsulated cipher is a block cipher, and the total number of
      * bytes written using one of the <code>write</code> methods is less than
      * the cipher's block size, no bytes will be written out.
      *
-     * @exception java.io.IOException if an I/O error occurs.
+     * @throws java.io.IOException if an I/O error occurs.
      */
     public void flush()
         throws IOException
     {
-        super.flush();
+        out.flush();
     }
 
     /**
      * Closes this output stream and releases any system resources
      * associated with this stream.
-     * <p>
+     * <p/>
      * This method invokes the <code>doFinal</code> method of the encapsulated
      * cipher object, which causes any bytes buffered by the encapsulated
      * cipher to be processed. The result is written out by calling the
      * <code>flush</code> method of this output stream.
-     * <p>
+     * <p/>
      * This method resets the encapsulated cipher object to its initial state
      * and calls the <code>close</code> method of the underlying output
      * stream.
      *
-     * @exception java.io.IOException if an I/O error occurs.
+     * @throws java.io.IOException if an I/O error occurs.
+     * @throws InvalidCipherTextIOException if the data written to this stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
      */
     public void close()
         throws IOException
     {
+        ensureCapacity(0);
+        IOException error = null;
         try
         {
             if (bufferedBlockCipher != null)
             {
-                byte[] buf = new byte[bufferedBlockCipher.getOutputSize(0)];
-
                 int outLen = bufferedBlockCipher.doFinal(buf, 0);
 
                 if (outLen != 0)
@@ -175,14 +226,41 @@
                     out.write(buf, 0, outLen);
                 }
             }
+            else if (aeadBlockCipher != null)
+            {
+                int outLen = aeadBlockCipher.doFinal(buf, 0);
+
+                if (outLen != 0)
+                {
+                    out.write(buf, 0, outLen);
+                }
+            }
+        }
+        catch (final InvalidCipherTextException e)
+        {
+            error = new InvalidCipherTextIOException("Error finalising cipher data", e);
         }
         catch (Exception e)
         {
-            throw new IOException("Error closing stream: " + e.toString());
+            error = new IOException("Error closing stream: " + e);
         }
 
-        flush();
-
-        super.close();
+        try
+        {
+            flush();
+            out.close();
+        }
+        catch (IOException e)
+        {
+            // Invalid ciphertext takes precedence over close error
+            if (error == null)
+            {
+                error = e;
+            }
+        }
+        if (error != null)
+        {
+            throw error;
+        }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
new file mode 100644
index 0000000..b601d4c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/io/InvalidCipherTextIOException.java
@@ -0,0 +1,28 @@
+package org.bouncycastle.crypto.io;
+
+import java.io.IOException;
+
+/**
+ * {@link IOException} wrapper around an exception indicating an invalid ciphertext, such as in
+ * authentication failure during finalisation of an AEAD cipher. For use in streams that need to
+ * expose invalid ciphertext errors.
+ */
+public class InvalidCipherTextIOException
+    extends IOException
+{
+    private static final long serialVersionUID = 1L;
+
+    private final Throwable cause;
+
+    public InvalidCipherTextIOException(String message, Throwable cause)
+    {
+        super(message);
+
+        this.cause = cause;
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html
deleted file mode 100644
index f2c9e40..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/io/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for doing "enhanced" I/O with Digests and MACs.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
index f4dfc6e..82ac20c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/kems/ECIESKeyEncapsulation.java
@@ -6,11 +6,13 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DerivationFunction;
 import org.bouncycastle.crypto.KeyEncapsulation;
+import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.crypto.params.KDFParameters;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.math.ec.ECCurve;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.BigIntegers;
 
@@ -106,33 +108,34 @@
             throw new IllegalArgumentException("Public key required for encryption");
         }
 
-        BigInteger n = key.getParameters().getN();
-        BigInteger h = key.getParameters().getH();
+        ECPublicKeyParameters ecPubKey = (ECPublicKeyParameters)key;
+        ECDomainParameters ecParams = ecPubKey.getParameters();
+        ECCurve curve = ecParams.getCurve();
+        BigInteger n = ecParams.getN();
+        BigInteger h = ecParams.getH();
 
         // Generate the ephemeral key pair    
         BigInteger r = BigIntegers.createRandomInRange(ONE, n, rnd);
-        ECPoint gTilde = key.getParameters().getG().multiply(r);
+
+        // Compute the static-ephemeral key agreement
+        BigInteger rPrime = CofactorMode ? r.multiply(h).mod(n) : r;
+
+        ECPoint[] ghTilde = new ECPoint[]{ 
+            ecParams.getG().multiply(r),
+            ecPubKey.getQ().multiply(rPrime)
+        };
+
+        // NOTE: More efficient than normalizing each individually
+        curve.normalizeAll(ghTilde);
+
+        ECPoint gTilde = ghTilde[0], hTilde = ghTilde[1];
 
         // Encode the ephemeral public key
         byte[] C = gTilde.getEncoded();
         System.arraycopy(C, 0, out, outOff, C.length);
 
-        // Compute the static-ephemeral key agreement
-        BigInteger rPrime;
-        if (CofactorMode)
-        {
-            rPrime = r.multiply(h).mod(n);
-        }
-        else
-        {
-            rPrime = r;
-        }
-
-        ECPoint hTilde = ((ECPublicKeyParameters)key).getQ().multiply(rPrime);
-
         // Encode the shared secret value
-        int PEHlen = (key.getParameters().getCurve().getFieldSize() + 7) / 8;
-        byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger());
+        byte[] PEH = hTilde.getAffineXCoord().getEncoded();
 
         // Initialise the KDF
         byte[] kdfInput;
@@ -186,40 +189,36 @@
             throw new IllegalArgumentException("Private key required for encryption");
         }
 
-        BigInteger n = key.getParameters().getN();
-        BigInteger h = key.getParameters().getH();
+        ECPrivateKeyParameters ecPrivKey = (ECPrivateKeyParameters)key;
+        ECDomainParameters ecParams = ecPrivKey.getParameters();
+        ECCurve curve = ecParams.getCurve();
+        BigInteger n = ecParams.getN();
+        BigInteger h = ecParams.getH();
 
         // Decode the ephemeral public key
         byte[] C = new byte[inLen];
         System.arraycopy(in, inOff, C, 0, inLen);
-        ECPoint gTilde = key.getParameters().getCurve().decodePoint(C);
+
+        // NOTE: Decoded points are already normalized (i.e in affine form)
+        ECPoint gTilde = curve.decodePoint(C);
 
         // Compute the static-ephemeral key agreement
-        ECPoint gHat;
+        ECPoint gHat = gTilde;
         if ((CofactorMode) || (OldCofactorMode))
         {
-            gHat = gTilde.multiply(h);
-        }
-        else
-        {
-            gHat = gTilde;
+            gHat = gHat.multiply(h);
         }
 
-        BigInteger xHat;
+        BigInteger xHat = ecPrivKey.getD();
         if (CofactorMode)
         {
-            xHat = ((ECPrivateKeyParameters)key).getD().multiply(h.modInverse(n)).mod(n);
-        }
-        else
-        {
-            xHat = ((ECPrivateKeyParameters)key).getD();
+            xHat = xHat.multiply(h.modInverse(n)).mod(n);
         }
 
-        ECPoint hTilde = gHat.multiply(xHat);
+        ECPoint hTilde = gHat.multiply(xHat).normalize();
 
         // Encode the shared secret value
-        int PEHlen = (key.getParameters().getCurve().getFieldSize() + 7) / 8;
-        byte[] PEH = BigIntegers.asUnsignedByteArray(PEHlen, hTilde.getX().toBigInteger());
+        byte[] PEH = hTilde.getAffineXCoord().getEncoded();
 
         // Initialise the KDF
         byte[] kdfInput;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html
deleted file mode 100644
index a5174b3..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/kems/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-The Key Encapsulation Mechanisms (KEMs) from ISO 18033-2.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
index 8a3b5bb..1aa8ede 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/CMac.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.paddings.ISO7816d4Padding;
+import org.bouncycastle.crypto.params.KeyParameter;
 
 /**
  * CMAC - as specified at www.nuee.nagoya-u.ac.jp/labs/tiwata/omac/omac.html
@@ -103,25 +104,36 @@
         return cipher.getAlgorithmName();
     }
 
+    private static int shiftLeft(byte[] block, byte[] output)
+    {
+        int i = block.length;
+        int bit = 0;
+        while (--i >= 0)
+        {
+            int b = block[i] & 0xff;
+            output[i] = (byte)((b << 1) | bit);
+            bit = (b >>> 7) & 1;
+        }
+        return bit;
+    }
+
     private static byte[] doubleLu(byte[] in)
     {
-        int FirstBit = (in[0] & 0xFF) >> 7;
         byte[] ret = new byte[in.length];
-        for (int i = 0; i < in.length - 1; i++)
-        {
-            ret[i] = (byte)((in[i] << 1) + ((in[i + 1] & 0xFF) >> 7));
-        }
-        ret[in.length - 1] = (byte)(in[in.length - 1] << 1);
-        if (FirstBit == 1)
-        {
-            ret[in.length - 1] ^= in.length == 16 ? CONSTANT_128 : CONSTANT_64;
-        }
+        int carry = shiftLeft(in, ret);
+        int xor = 0xff & (in.length == 16 ? CONSTANT_128 : CONSTANT_64);
+
+        /*
+         * NOTE: This construction is an attempt at a constant-time implementation.
+         */
+        ret[in.length - 1] ^= (xor >>> ((1 - carry) << 3));
+
         return ret;
     }
 
     public void init(CipherParameters params)
     {
-        if (params != null)
+        if (params instanceof KeyParameter)
         {
             cipher.init(true, params);
     
@@ -130,6 +142,10 @@
             cipher.processBlock(ZEROES, 0, L, 0);
             Lu = doubleLu(L);
             Lu2 = doubleLu(Lu);
+        } else if (params != null)
+        {
+            // CMAC mode does not permit IV to underlying CBC mode
+            throw new IllegalArgumentException("CMac mode only permits key to be set.");
         }
 
         reset();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
new file mode 100644
index 0000000..150eb61
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/Poly1305.java
@@ -0,0 +1,279 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.util.Pack;
+
+/**
+ * Poly1305 message authentication code, designed by D. J. Bernstein.
+ * <p>
+ * Poly1305 computes a 128-bit (16 bytes) authenticator, using a 128 bit nonce and a 256 bit key
+ * consisting of a 128 bit key applied to an underlying cipher, and a 128 bit key (with 106
+ * effective key bits) used in the authenticator.
+ * <p>
+ * The polynomial calculation in this implementation is adapted from the public domain <a
+ * href="https://github.com/floodyberry/poly1305-donna">poly1305-donna-unrolled</a> C implementation
+ * by Andrew M (@floodyberry).
+ * @see Poly1305KeyGenerator
+ */
+public class Poly1305
+    implements Mac
+{
+    private static final int BLOCK_SIZE = 16;
+
+    private final BlockCipher cipher;
+
+    private final byte[] singleByte = new byte[1];
+
+    // Initialised state
+
+    /** Polynomial key */
+    private int r0, r1, r2, r3, r4;
+
+    /** Precomputed 5 * r[1..4] */
+    private int s1, s2, s3, s4;
+
+    /** Encrypted nonce */
+    private int k0, k1, k2, k3;
+
+    // Accumulating state
+
+    /** Current block of buffered input */
+    private final byte[] currentBlock = new byte[BLOCK_SIZE];
+
+    /** Current offset in input buffer */
+    private int currentBlockOffset = 0;
+
+    /** Polynomial accumulator */
+    private int h0, h1, h2, h3, h4;
+
+    /**
+     * Constructs a Poly1305 MAC, using a 128 bit block cipher.
+     */
+    public Poly1305(final BlockCipher cipher)
+    {
+        if (cipher.getBlockSize() != BLOCK_SIZE)
+        {
+            throw new IllegalArgumentException("Poly1305 requires a 128 bit block cipher.");
+        }
+        this.cipher = cipher;
+    }
+
+    /**
+     * Initialises the Poly1305 MAC.
+     *
+     * @param a {@link ParametersWithIV} containing a 128 bit nonce and a {@link KeyParameter} with
+     *            a 256 bit key complying to the {@link Poly1305KeyGenerator Poly1305 key format}.
+     */
+    public void init(final CipherParameters params)
+        throws IllegalArgumentException
+    {
+        final byte[] nonce;
+        final byte[] key;
+        if ((params instanceof ParametersWithIV) && ((ParametersWithIV)params).getParameters() instanceof KeyParameter)
+        {
+            nonce = ((ParametersWithIV)params).getIV();
+            key = ((KeyParameter)((ParametersWithIV)params).getParameters()).getKey();
+        }
+        else
+        {
+            throw new IllegalArgumentException("Poly1305 requires a key and and IV.");
+        }
+
+        setKey(key, nonce);
+        reset();
+    }
+
+    private void setKey(final byte[] key, final byte[] nonce)
+    {
+        if (nonce.length != BLOCK_SIZE)
+        {
+            throw new IllegalArgumentException("Poly1305 requires a 128 bit IV.");
+        }
+        Poly1305KeyGenerator.checkKey(key);
+
+        // Extract r portion of key
+        int t0 = Pack.littleEndianToInt(key, BLOCK_SIZE + 0);
+        int t1 = Pack.littleEndianToInt(key, BLOCK_SIZE + 4);
+        int t2 = Pack.littleEndianToInt(key, BLOCK_SIZE + 8);
+        int t3 = Pack.littleEndianToInt(key, BLOCK_SIZE + 12);
+
+        r0 = t0 & 0x3ffffff; t0 >>>= 26; t0 |= t1 << 6;
+        r1 = t0 & 0x3ffff03; t1 >>>= 20; t1 |= t2 << 12;
+        r2 = t1 & 0x3ffc0ff; t2 >>>= 14; t2 |= t3 << 18;
+        r3 = t2 & 0x3f03fff; t3 >>>= 8;
+        r4 = t3 & 0x00fffff;
+
+        // Precompute multipliers
+        s1 = r1 * 5;
+        s2 = r2 * 5;
+        s3 = r3 * 5;
+        s4 = r4 * 5;
+
+        // Compute encrypted nonce
+        final byte[] cipherKey = new byte[BLOCK_SIZE];
+        System.arraycopy(key, 0, cipherKey, 0, cipherKey.length);
+
+        cipher.init(true, new KeyParameter(cipherKey));
+        cipher.processBlock(nonce, 0, cipherKey, 0);
+
+        k0 = Pack.littleEndianToInt(cipherKey, 0);
+        k1 = Pack.littleEndianToInt(cipherKey, 4);
+        k2 = Pack.littleEndianToInt(cipherKey, 8);
+        k3 = Pack.littleEndianToInt(cipherKey, 12);
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Poly1305-" + cipher.getAlgorithmName();
+    }
+
+    public int getMacSize()
+    {
+        return BLOCK_SIZE;
+    }
+
+    public void update(final byte in)
+        throws IllegalStateException
+    {
+        singleByte[0] = in;
+        update(singleByte, 0, 1);
+    }
+
+    public void update(final byte[] in, final int inOff, final int len)
+        throws DataLengthException,
+        IllegalStateException
+    {
+        int copied = 0;
+        while (len > copied)
+        {
+            if (currentBlockOffset == BLOCK_SIZE)
+            {
+                processBlock();
+                currentBlockOffset = 0;
+            }
+
+            int toCopy = Math.min((len - copied), BLOCK_SIZE - currentBlockOffset);
+            System.arraycopy(in, copied + inOff, currentBlock, currentBlockOffset, toCopy);
+            copied += toCopy;
+            currentBlockOffset += toCopy;
+        }
+
+    }
+
+    private void processBlock()
+    {
+        if (currentBlockOffset < BLOCK_SIZE)
+        {
+            currentBlock[currentBlockOffset] = 1;
+            for (int i = currentBlockOffset + 1; i < BLOCK_SIZE; i++)
+            {
+                currentBlock[i] = 0;
+            }
+        }
+
+        final long t0 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 0);
+        final long t1 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 4);
+        final long t2 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 8);
+        final long t3 = 0xffffffffL & Pack.littleEndianToInt(currentBlock, 12);
+
+        h0 += t0 & 0x3ffffff;
+        h1 += (((t1 << 32) | t0) >>> 26) & 0x3ffffff;
+        h2 += (((t2 << 32) | t1) >>> 20) & 0x3ffffff;
+        h3 += (((t3 << 32) | t2) >>> 14) & 0x3ffffff;
+        h4 += (t3 >>> 8);
+
+        if (currentBlockOffset == BLOCK_SIZE)
+        {
+            h4 += (1 << 24);
+        }
+
+        long tp0 = mul32x32_64(h0,r0) + mul32x32_64(h1,s4) + mul32x32_64(h2,s3) + mul32x32_64(h3,s2) + mul32x32_64(h4,s1);
+        long tp1 = mul32x32_64(h0,r1) + mul32x32_64(h1,r0) + mul32x32_64(h2,s4) + mul32x32_64(h3,s3) + mul32x32_64(h4,s2);
+        long tp2 = mul32x32_64(h0,r2) + mul32x32_64(h1,r1) + mul32x32_64(h2,r0) + mul32x32_64(h3,s4) + mul32x32_64(h4,s3);
+        long tp3 = mul32x32_64(h0,r3) + mul32x32_64(h1,r2) + mul32x32_64(h2,r1) + mul32x32_64(h3,r0) + mul32x32_64(h4,s4);
+        long tp4 = mul32x32_64(h0,r4) + mul32x32_64(h1,r3) + mul32x32_64(h2,r2) + mul32x32_64(h3,r1) + mul32x32_64(h4,r0);
+
+        long b;
+        h0 = (int)tp0 & 0x3ffffff; b = (tp0 >>> 26);
+        tp1 += b; h1 = (int)tp1 & 0x3ffffff; b = ((tp1 >>> 26) & 0xffffffff);
+        tp2 += b; h2 = (int)tp2 & 0x3ffffff; b = ((tp2 >>> 26) & 0xffffffff);
+        tp3 += b; h3 = (int)tp3 & 0x3ffffff; b = (tp3 >>> 26);
+        tp4 += b; h4 = (int)tp4 & 0x3ffffff; b = (tp4 >>> 26);
+        h0 += b * 5;
+    }
+
+    public int doFinal(final byte[] out, final int outOff)
+        throws DataLengthException,
+        IllegalStateException
+    {
+        if (outOff + BLOCK_SIZE > out.length)
+        {
+            throw new DataLengthException("Output buffer is too short.");
+        }
+
+        if (currentBlockOffset > 0)
+        {
+            // Process padded final block
+            processBlock();
+        }
+
+        long f0, f1, f2, f3;
+
+        int b = h0 >>> 26;
+        h0 = h0 & 0x3ffffff;
+        h1 += b; b = h1 >>> 26; h1 = h1 & 0x3ffffff;
+        h2 += b; b = h2 >>> 26; h2 = h2 & 0x3ffffff;
+        h3 += b; b = h3 >>> 26; h3 = h3 & 0x3ffffff;
+        h4 += b; b = h4 >>> 26; h4 = h4 & 0x3ffffff;
+        h0 += b * 5;
+
+        int g0, g1, g2, g3, g4;
+        g0 = h0 + 5; b = g0 >>> 26; g0 &= 0x3ffffff;
+        g1 = h1 + b; b = g1 >>> 26; g1 &= 0x3ffffff;
+        g2 = h2 + b; b = g2 >>> 26; g2 &= 0x3ffffff;
+        g3 = h3 + b; b = g3 >>> 26; g3 &= 0x3ffffff;
+        g4 = h4 + b - (1 << 26);
+
+        b = (g4 >>> 31) - 1;
+        int nb = ~b;
+        h0 = (h0 & nb) | (g0 & b);
+        h1 = (h1 & nb) | (g1 & b);
+        h2 = (h2 & nb) | (g2 & b);
+        h3 = (h3 & nb) | (g3 & b);
+        h4 = (h4 & nb) | (g4 & b);
+
+        f0 = (((h0       ) | (h1 << 26)) & 0xffffffffl) + (0xffffffffL & k0);
+        f1 = (((h1 >>> 6 ) | (h2 << 20)) & 0xffffffffl) + (0xffffffffL & k1);
+        f2 = (((h2 >>> 12) | (h3 << 14)) & 0xffffffffl) + (0xffffffffL & k2);
+        f3 = (((h3 >>> 18) | (h4 << 8 )) & 0xffffffffl) + (0xffffffffL & k3);
+
+        Pack.intToLittleEndian((int)f0, out, outOff);
+        f1 += (f0 >>> 32);
+        Pack.intToLittleEndian((int)f1, out, outOff + 4);
+        f2 += (f1 >>> 32);
+        Pack.intToLittleEndian((int)f2, out, outOff + 8);
+        f3 += (f2 >>> 32);
+        Pack.intToLittleEndian((int)f3, out, outOff + 12);
+
+        reset();
+        return BLOCK_SIZE;
+    }
+
+    public void reset()
+    {
+        currentBlockOffset = 0;
+
+        h0 = h1 = h2 = h3 = h4 = 0;
+    }
+
+    private static final long mul32x32_64(int i1, int i2)
+    {
+        return ((long)i1) * i2;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
new file mode 100644
index 0000000..6097247
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/macs/SkeinMac.java
@@ -0,0 +1,119 @@
+package org.bouncycastle.crypto.macs;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.Mac;
+import org.bouncycastle.crypto.digests.SkeinEngine;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.SkeinParameters;
+
+/**
+ * Implementation of the Skein parameterised MAC function in 256, 512 and 1024 bit block sizes,
+ * based on the {@link ThreefishEngine Threefish} tweakable block cipher.
+ * <p/>
+ * This is the 1.3 version of Skein defined in the Skein hash function submission to the NIST SHA-3
+ * competition in October 2010.
+ * <p/>
+ * Skein was designed by Niels Ferguson - Stefan Lucks - Bruce Schneier - Doug Whiting - Mihir
+ * Bellare - Tadayoshi Kohno - Jon Callas - Jesse Walker.
+ * <p/>
+ *
+ * @see SkeinEngine
+ * @see SkeinParameters
+ */
+public class SkeinMac
+    implements Mac
+{
+    /**
+     * 256 bit block size - Skein MAC-256
+     */
+    public static final int SKEIN_256 = SkeinEngine.SKEIN_256;
+    /**
+     * 512 bit block size - Skein MAC-512
+     */
+    public static final int SKEIN_512 = SkeinEngine.SKEIN_512;
+    /**
+     * 1024 bit block size - Skein MAC-1024
+     */
+    public static final int SKEIN_1024 = SkeinEngine.SKEIN_1024;
+
+    private SkeinEngine engine;
+
+    /**
+     * Constructs a Skein MAC with an internal state size and output size.
+     *
+     * @param stateSizeBits  the internal state size in bits - one of {@link #SKEIN_256}, {@link #SKEIN_512} or
+     *                       {@link #SKEIN_1024}.
+     * @param digestSizeBits the output/MAC size to produce in bits, which must be an integral number of bytes.
+     */
+    public SkeinMac(int stateSizeBits, int digestSizeBits)
+    {
+        this.engine = new SkeinEngine(stateSizeBits, digestSizeBits);
+    }
+
+    public SkeinMac(SkeinMac mac)
+    {
+        this.engine = new SkeinEngine(mac.engine);
+    }
+
+    public String getAlgorithmName()
+    {
+        return "Skein-MAC-" + (engine.getBlockSize() * 8) + "-" + (engine.getOutputSize() * 8);
+    }
+
+    /**
+     * Initialises the Skein digest with the provided parameters.<br>
+     * See {@link SkeinParameters} for details on the parameterisation of the Skein hash function.
+     *
+     * @param params an instance of {@link SkeinParameters} or {@link KeyParameter}.
+     */
+    public void init(CipherParameters params)
+        throws IllegalArgumentException
+    {
+        SkeinParameters skeinParameters;
+        if (params instanceof SkeinParameters)
+        {
+            skeinParameters = (SkeinParameters)params;
+        }
+        else if (params instanceof KeyParameter)
+        {
+            skeinParameters = new SkeinParameters.Builder().setKey(((KeyParameter)params).getKey()).build();
+        }
+        else
+        {
+            throw new IllegalArgumentException("Invalid parameter passed to Skein MAC init - "
+                + params.getClass().getName());
+        }
+        if (skeinParameters.getKey() == null)
+        {
+            throw new IllegalArgumentException("Skein MAC requires a key parameter.");
+        }
+        engine.init(skeinParameters);
+    }
+
+    public int getMacSize()
+    {
+        return engine.getOutputSize();
+    }
+
+    public void reset()
+    {
+        engine.reset();
+    }
+
+    public void update(byte in)
+    {
+        engine.update(in);
+    }
+
+    public void update(byte[] in, int inOff, int len)
+    {
+        engine.update(in, inOff, len);
+    }
+
+    public int doFinal(byte[] out, int outOff)
+    {
+        return engine.doFinal(out, outOff);
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html
deleted file mode 100644
index 0b1f86d..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/macs/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for creating MACs and HMACs.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
index 9a6e2e0..fef51fd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CCMBlockCipher.java
@@ -29,8 +29,8 @@
     private int                   macSize;
     private CipherParameters      keyParam;
     private byte[]                macBlock;
-    private ByteArrayOutputStream associatedText = new ByteArrayOutputStream();
-    private ByteArrayOutputStream data = new ByteArrayOutputStream();
+    private ExposedByteArrayOutputStream associatedText = new ExposedByteArrayOutputStream();
+    private ExposedByteArrayOutputStream data = new ExposedByteArrayOutputStream();
 
     /**
      * Basic constructor.
@@ -65,6 +65,7 @@
     {
         this.forEncryption = forEncryption;
 
+        CipherParameters cipherParameters;
         if (params instanceof AEADParameters)
         {
             AEADParameters param = (AEADParameters)params;
@@ -72,7 +73,7 @@
             nonce = param.getNonce();
             initialAssociatedText = param.getAssociatedText();
             macSize = param.getMacSize() / 8;
-            keyParam = param.getKey();
+            cipherParameters = param.getKey();
         }
         else if (params instanceof ParametersWithIV)
         {
@@ -81,17 +82,25 @@
             nonce = param.getIV();
             initialAssociatedText = null;
             macSize = macBlock.length / 2;
-            keyParam = param.getParameters();
+            cipherParameters = param.getParameters();
         }
         else
         {
             throw new IllegalArgumentException("invalid parameters passed to CCM");
         }
 
+        // NOTE: Very basic support for key re-use, but no performance gain from it
+        if (cipherParameters != null)
+        {
+            keyParam = cipherParameters;
+        }
+
         if (nonce == null || nonce.length < 7 || nonce.length > 13)
         {
             throw new IllegalArgumentException("nonce must have length from 7 to 13 octets");
         }
+        
+        reset();
     }
 
     public String getAlgorithmName()
@@ -129,14 +138,11 @@
     public int doFinal(byte[] out, int outOff)
         throws IllegalStateException, InvalidCipherTextException
     {
-        byte[] text = data.toByteArray();
-        byte[] enc = processPacket(text, 0, text.length);
-
-        System.arraycopy(enc, 0, out, outOff, enc.length);
+        int len = processPacket(data.getBuffer(), 0, data.size(), out, outOff);
 
         reset();
 
-        return enc.length;
+        return len;
     }
 
     public void reset()
@@ -178,9 +184,55 @@
         return totalData < macSize ? 0 : totalData - macSize;
     }
 
+    /**
+     * Process a packet of data for either CCM decryption or encryption.
+     *
+     * @param in data for processing.
+     * @param inOff offset at which data starts in the input array.
+     * @param inLen length of the data in the input array.
+     * @return a byte array containing the processed input..
+     * @throws IllegalStateException if the cipher is not appropriately set up.
+     * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+     */
     public byte[] processPacket(byte[] in, int inOff, int inLen)
         throws IllegalStateException, InvalidCipherTextException
     {
+        byte[] output;
+
+        if (forEncryption)
+        {
+            output = new byte[inLen + macSize];
+        }
+        else
+        {
+            if (inLen < macSize)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
+            output = new byte[inLen - macSize];
+        }
+
+        processPacket(in, inOff, inLen, output, 0);
+
+        return output;
+    }
+
+    /**
+     * Process a packet of data for either CCM decryption or encryption.
+     *
+     * @param in data for processing.
+     * @param inOff offset at which data starts in the input array.
+     * @param inLen length of the data in the input array.
+     * @param output output array.
+     * @param outOff offset into output array to start putting processed bytes.
+     * @return the number of bytes added to output.
+     * @throws IllegalStateException if the cipher is not appropriately set up.
+     * @throws InvalidCipherTextException if the input data is truncated or the mac check fails.
+     * @throws DataLengthException if output buffer too short.
+     */
+    public int processPacket(byte[] in, int inOff, int inLen, byte[] output, int outOff)
+        throws IllegalStateException, InvalidCipherTextException, DataLengthException
+    {
         // TODO: handle null keyParam (e.g. via RepeatedKeySpec)
         // Need to keep the CTR and CBC Mac parts around and reset
         if (keyParam == null)
@@ -206,42 +258,52 @@
         BlockCipher ctrCipher = new SICBlockCipher(cipher);
         ctrCipher.init(forEncryption, new ParametersWithIV(keyParam, iv));
 
-        int index = inOff;
-        int outOff = 0;
-        byte[] output;
+        int outputLen;
+        int inIndex = inOff;
+        int outIndex = outOff;
 
         if (forEncryption)
         {
-            output = new byte[inLen + macSize];
+            outputLen = inLen + macSize;
+            if (output.length < (outputLen + outOff))
+            {
+                throw new DataLengthException("Output buffer too short.");
+            }
 
             calculateMac(in, inOff, inLen, macBlock);
 
             ctrCipher.processBlock(macBlock, 0, macBlock, 0);   // S0
 
-            while (index < inLen - blockSize)                   // S1...
+            while (inIndex < (inOff + inLen - blockSize))                 // S1...
             {
-                ctrCipher.processBlock(in, index, output, outOff);
-                outOff += blockSize;
-                index += blockSize;
+                ctrCipher.processBlock(in, inIndex, output, outIndex);
+                outIndex += blockSize;
+                inIndex += blockSize;
             }
 
             byte[] block = new byte[blockSize];
 
-            System.arraycopy(in, index, block, 0, inLen - index);
+            System.arraycopy(in, inIndex, block, 0, inLen + inOff - inIndex);
 
             ctrCipher.processBlock(block, 0, block, 0);
 
-            System.arraycopy(block, 0, output, outOff, inLen - index);
+            System.arraycopy(block, 0, output, outIndex, inLen + inOff - inIndex);
 
-            outOff += inLen - index;
-
-            System.arraycopy(macBlock, 0, output, outOff, output.length - outOff);
+            System.arraycopy(macBlock, 0, output, outOff + inLen, macSize);
         }
         else
         {
-            output = new byte[inLen - macSize];
+            if (inLen < macSize)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
+            outputLen = inLen - macSize;
+            if (output.length < (outputLen + outOff))
+            {
+                throw new DataLengthException("Output buffer too short.");
+            }
 
-            System.arraycopy(in, inOff + inLen - macSize, macBlock, 0, macSize);
+            System.arraycopy(in, inOff + outputLen, macBlock, 0, macSize);
 
             ctrCipher.processBlock(macBlock, 0, macBlock, 0);
 
@@ -250,24 +312,24 @@
                 macBlock[i] = 0;
             }
 
-            while (outOff < output.length - blockSize)
+            while (inIndex < (inOff + outputLen - blockSize))
             {
-                ctrCipher.processBlock(in, index, output, outOff);
-                outOff += blockSize;
-                index += blockSize;
+                ctrCipher.processBlock(in, inIndex, output, outIndex);
+                outIndex += blockSize;
+                inIndex += blockSize;
             }
 
             byte[] block = new byte[blockSize];
 
-            System.arraycopy(in, index, block, 0, output.length - outOff);
+            System.arraycopy(in, inIndex, block, 0, outputLen - (inIndex - inOff));
 
             ctrCipher.processBlock(block, 0, block, 0);
 
-            System.arraycopy(block, 0, output, outOff, output.length - outOff);
+            System.arraycopy(block, 0, output, outIndex, outputLen - (inIndex - inOff));
 
             byte[] calculatedMacBlock = new byte[blockSize];
 
-            calculateMac(output, 0, output.length, calculatedMacBlock);
+            calculateMac(output, outOff, outputLen, calculatedMacBlock);
 
             if (!Arrays.constantTimeAreEqual(macBlock, calculatedMacBlock))
             {
@@ -275,7 +337,7 @@
             }
         }
 
-        return output;
+        return outputLen;
     }
 
     private int calculateMac(byte[] data, int dataOff, int dataLen, byte[] macBlock)
@@ -344,8 +406,7 @@
             }
             if (associatedText.size() > 0)
             {
-                byte[] tmp = associatedText.toByteArray();
-                cMac.update(tmp, 0, tmp.length);
+                cMac.update(associatedText.getBuffer(), 0, associatedText.size());
             }
 
             extra = (extra + textLength) % 16;
@@ -375,4 +436,17 @@
     {
         return getAssociatedTextLength() > 0;
     }
+
+    private class ExposedByteArrayOutputStream
+        extends ByteArrayOutputStream
+    {
+        public ExposedByteArrayOutputStream()
+        {
+        }
+
+        public byte[] getBuffer()
+        {
+            return this.buf;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
index d0fb9bb..a885169 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CFBBlockCipher.java
@@ -4,6 +4,7 @@
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DataLengthException;
 import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.util.Arrays;
 
 /**
  * implements a Cipher-FeedBack (CFB) mode on top of a simple cipher.
@@ -246,6 +247,16 @@
     }
 
     /**
+     * Return the current state of the initialisation vector.
+     *
+     * @return current IV
+     */
+    public byte[] getCurrentIV()
+    {
+        return Arrays.clone(cfbV);
+    }
+
+    /**
      * reset the chaining vector back to the IV and reset the underlying
      * cipher.
      */
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
index b8e5b61..5388b40 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/CTSBlockCipher.java
@@ -22,8 +22,9 @@
     public CTSBlockCipher(
         BlockCipher     cipher)
     {
-        if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher))
+        if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher) || (cipher instanceof SICBlockCipher))
         {
+            // TODO: This is broken - need to introduce marker interface to differentiate block cipher primitive from mode?
             throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
         }
 
@@ -72,7 +73,7 @@
     }
 
     /**
-     * process a single byte, producing an output block if neccessary.
+     * process a single byte, producing an output block if necessary.
      *
      * @param in the input byte.
      * @param out the space for any output that might be produced.
@@ -200,60 +201,81 @@
 
         if (forEncryption)
         {
-            cipher.processBlock(buf, 0, block, 0);
-            
             if (bufOff < blockSize)
             {
                 throw new DataLengthException("need at least one block of input for CTS");
             }
 
-            for (int i = bufOff; i != buf.length; i++)
-            {
-                buf[i] = block[i - blockSize];
-            }
+            cipher.processBlock(buf, 0, block, 0);
 
-            for (int i = blockSize; i != bufOff; i++)
+            if (bufOff > blockSize)
             {
-                buf[i] ^= block[i - blockSize];
-            }
+                for (int i = bufOff; i != buf.length; i++)
+                {
+                    buf[i] = block[i - blockSize];
+                }
 
-            if (cipher instanceof CBCBlockCipher)
-            {
-                BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                for (int i = blockSize; i != bufOff; i++)
+                {
+                    buf[i] ^= block[i - blockSize];
+                }
 
-                c.processBlock(buf, blockSize, out, outOff);
+                if (cipher instanceof CBCBlockCipher)
+                {
+                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+                    c.processBlock(buf, blockSize, out, outOff);
+                }
+                else
+                {
+                    cipher.processBlock(buf, blockSize, out, outOff);
+                }
+
+                System.arraycopy(block, 0, out, outOff + blockSize, len);
             }
             else
             {
-                cipher.processBlock(buf, blockSize, out, outOff);
+                System.arraycopy(block, 0, out, outOff, blockSize);
             }
-
-            System.arraycopy(block, 0, out, outOff + blockSize, len);
         }
         else
         {
+            if (bufOff < blockSize)
+            {
+                throw new DataLengthException("need at least one block of input for CTS");
+            }
+
             byte[]  lastBlock = new byte[blockSize];
 
-            if (cipher instanceof CBCBlockCipher)
+            if (bufOff > blockSize)
             {
-                BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+                if (cipher instanceof CBCBlockCipher)
+                {
+                    BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
 
-                c.processBlock(buf, 0, block, 0);
+                    c.processBlock(buf, 0, block, 0);
+                }
+                else
+                {
+                    cipher.processBlock(buf, 0, block, 0);
+                }
+
+                for (int i = blockSize; i != bufOff; i++)
+                {
+                    lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+                }
+
+                System.arraycopy(buf, blockSize, block, 0, len);
+
+                cipher.processBlock(block, 0, out, outOff);
+                System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
             }
             else
             {
                 cipher.processBlock(buf, 0, block, 0);
+
+                System.arraycopy(block, 0, out, outOff, blockSize);
             }
-
-            for (int i = blockSize; i != bufOff; i++)
-            {
-                lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
-            }
-
-            System.arraycopy(buf, blockSize, block, 0, len);
-
-            cipher.processBlock(block, 0, out, outOff);
-            System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
         }
 
         int offset = bufOff;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
index 4999caa..8f74000 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/EAXBlockCipher.java
@@ -123,16 +123,10 @@
         mac.update(nonce, 0, nonce.length);
         mac.doFinal(nonceMac, 0);
 
-        tag[blockSize - 1] = hTAG;
-        mac.update(tag, 0, blockSize);
-
-        if (initialAssociatedText != null)
-        {
-            processAADBytes(initialAssociatedText, 0, initialAssociatedText.length);
-        }
-
         // Same BlockCipher underlies this and the mac, so reuse last key on cipher 
         cipher.init(true, new ParametersWithIV(null, nonceMac));
+        
+        reset();
     }
 
     private void initCipher()
@@ -206,7 +200,7 @@
     {
         if (cipherInitialized)
         {
-            throw new IllegalStateException("AAD data cannot be added after encryption/decription processing has begun.");
+            throw new IllegalStateException("AAD data cannot be added after encryption/decryption processing has begun.");
         }
         mac.update(in, inOff, len);
     }
@@ -246,6 +240,10 @@
 
         if (forEncryption)
         {
+            if (out.length < (outOff + extra))
+            {
+                throw new DataLengthException("Output buffer too short");
+            }
             cipher.processBlock(bufBlock, 0, tmp, 0);
             cipher.processBlock(bufBlock, blockSize, tmp, blockSize);
 
@@ -263,6 +261,10 @@
         }
         else
         {
+            if (extra < macSize)
+            {
+                throw new InvalidCipherTextException("data too short");
+            }
             if (extra > macSize)
             {
                 mac.update(bufBlock, 0, extra - macSize);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
new file mode 100644
index 0000000..887c169
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GCFBBlockCipher.java
@@ -0,0 +1,109 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.params.ParametersWithSBox;
+
+/**
+ * An implementation of the GOST CFB mode with CryptoPro key meshing as described in RFC 4357.
+ */
+public class GCFBBlockCipher
+    implements BlockCipher
+{
+    private static final byte[] C =
+        {
+            0x69, 0x00, 0x72, 0x22, 0x64, (byte)0xC9, 0x04, 0x23,
+            (byte)0x8D, 0x3A, (byte)0xDB, (byte)0x96, 0x46, (byte)0xE9, 0x2A, (byte)0xC4,
+            0x18, (byte)0xFE, (byte)0xAC, (byte)0x94, 0x00, (byte)0xED, 0x07, 0x12,
+            (byte)0xC0, (byte)0x86, (byte)0xDC, (byte)0xC2, (byte)0xEF, 0x4C, (byte)0xA9, 0x2B
+        };
+
+    private final CFBBlockCipher cfbEngine;
+
+    private KeyParameter key;
+    private long         counter = 0;
+    private boolean      forEncryption;
+
+    public GCFBBlockCipher(BlockCipher engine)
+    {
+        this.cfbEngine = new CFBBlockCipher(engine, engine.getBlockSize() * 8);
+    }
+
+    public void init(boolean forEncryption, CipherParameters params)
+        throws IllegalArgumentException
+    {
+        counter = 0;
+        cfbEngine.init(forEncryption, params);
+
+        this.forEncryption = forEncryption;
+
+        if (params instanceof ParametersWithIV)
+        {
+            params = ((ParametersWithIV)params).getParameters();
+        }
+
+        if (params instanceof ParametersWithRandom)
+        {
+            params = ((ParametersWithRandom)params).getParameters();
+        }
+
+        if (params instanceof ParametersWithSBox)
+        {
+            params = ((ParametersWithSBox)params).getParameters();
+        }
+
+        key = (KeyParameter)params;
+    }
+
+    public String getAlgorithmName()
+    {
+        return "G" + cfbEngine.getAlgorithmName();
+    }
+
+    public int getBlockSize()
+    {
+        return cfbEngine.getBlockSize();
+    }
+
+    public int processBlock(byte[] in, int inOff, byte[] out, int outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (counter > 0 && counter % 1024 == 0)
+        {
+            BlockCipher  base = cfbEngine.getUnderlyingCipher();
+
+            base.init(false, key);
+
+            byte[] nextKey = new byte[32];
+
+            base.processBlock(C, 0, nextKey, 0);
+            base.processBlock(C, 8, nextKey, 8);
+            base.processBlock(C, 16, nextKey, 16);
+            base.processBlock(C, 24, nextKey, 24);
+
+            key = new KeyParameter(nextKey);
+
+            byte[] iv = new byte[8];
+
+            base.init(true, key);
+
+            base.processBlock(cfbEngine.getCurrentIV(), 0, iv, 0);
+
+            cfbEngine.init(forEncryption, new ParametersWithIV(key, iv));
+        }
+
+        counter += cfbEngine.getBlockSize();
+
+        return cfbEngine.processBlock(in, inOff, out, outOff);
+    }
+
+    public void reset()
+    {
+        counter = 0;
+        cfbEngine.reset();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
index 1178974..0e66cf3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/GOFBBlockCipher.java
@@ -206,6 +206,9 @@
      */
     public void reset()
     {
+        firstStep = true;
+        N3 = 0;
+        N4 = 0;
         System.arraycopy(IV, 0, ofbV, 0, IV.length);
 
         cipher.reset();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
index d4d2910..6dc7148 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OCBBlockCipher.java
@@ -13,7 +13,7 @@
 
 /**
  * An implementation of the "work in progress" Internet-Draft <a
- * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-00">The OCB Authenticated-Encryption
+ * href="http://tools.ietf.org/html/draft-irtf-cfrg-ocb-03">The OCB Authenticated-Encryption
  * Algorithm</a>, licensed per:
  * <p/>
  * <blockquote> <a href="http://www.cs.ucdavis.edu/~rogaway/ocb/license1.pdf">License for
@@ -111,7 +111,6 @@
     public void init(boolean forEncryption, CipherParameters parameters)
         throws IllegalArgumentException
     {
-
         this.forEncryption = forEncryption;
         this.macBlock = null;
 
@@ -156,23 +155,18 @@
             N = new byte[0];
         }
 
-        if (N.length > 16 || (N.length == 16 && (N[0] & 0x80) != 0))
+        if (N.length > 15)
         {
-            /*
-             * NOTE: We don't just ignore bit 128 because it would hide from the caller the fact
-             * that two nonces differing only in bit 128 are not different.
-             */
-            throw new IllegalArgumentException("IV must be no more than 127 bits");
+            throw new IllegalArgumentException("IV must be no more than 15 bytes");
         }
 
         /*
          * KEY-DEPENDENT INITIALISATION
          */
 
-        // if keyParam is null we're reusing the last key.
-        if (keyParameter != null)
+        if (keyParameter == null)
         {
-            // TODO
+            // TODO If 'keyParameter' is null we're re-using the last key.
         }
 
         // hashCipher always used in forward mode
@@ -193,17 +187,10 @@
 
         byte[] nonce = new byte[16];
         System.arraycopy(N, 0, nonce, nonce.length - N.length, N.length);
-        if (N.length == 16)
-        {
-            nonce[0] &= 0x80;
-        }
-        else
-        {
-            nonce[15 - N.length] = 1;
-        }
+        nonce[0] = (byte)(macSize << 4);
+        nonce[15 - N.length] |= 1;
 
         int bottom = nonce[15] & 0x3F;
-        // System.out.println("bottom: " + bottom);
 
         byte[] Ktop = new byte[16];
         nonce[15] &= 0xC0;
@@ -314,7 +301,6 @@
     public int processBytes(byte[] input, int inOff, int len, byte[] output, int outOff)
         throws DataLengthException
     {
-
         int resultLen = 0;
 
         for (int i = 0; i < len; ++i)
@@ -334,7 +320,6 @@
         throws IllegalStateException,
         InvalidCipherTextException
     {
-
         /*
          * For decryption, get the tag from the end of the message
          */
@@ -483,7 +468,6 @@
 
     protected void reset(boolean clearMac)
     {
-
         hashCipher.reset();
         mainCipher.reset();
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
new file mode 100644
index 0000000..b34432a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/OldCTSBlockCipher.java
@@ -0,0 +1,269 @@
+package org.bouncycastle.crypto.modes;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.BufferedBlockCipher;
+import org.bouncycastle.crypto.DataLengthException;
+import org.bouncycastle.crypto.InvalidCipherTextException;
+
+/**
+ * A Cipher Text Stealing (CTS) mode cipher. CTS allows block ciphers to
+ * be used to produce cipher text which is the same length as the plain text.
+ * <p>
+ * This version applies the CTS algorithm from one block up, rather than following the errata update issued in 2004, where CTS mode is applied
+ * from greater than 1 block up and the first block is processed using CBC mode.
+ * </p>
+ */
+public class OldCTSBlockCipher
+    extends BufferedBlockCipher
+{
+    private int     blockSize;
+
+    /**
+     * Create a buffered block cipher that uses Cipher Text Stealing
+     *
+     * @param cipher the underlying block cipher this buffering object wraps.
+     */
+    public OldCTSBlockCipher(
+        BlockCipher cipher)
+    {
+        if ((cipher instanceof OFBBlockCipher) || (cipher instanceof CFBBlockCipher))
+        {
+            throw new IllegalArgumentException("CTSBlockCipher can only accept ECB, or CBC ciphers");
+        }
+
+        this.cipher = cipher;
+
+        blockSize = cipher.getBlockSize();
+
+        buf = new byte[blockSize * 2];
+        bufOff = 0;
+    }
+
+    /**
+     * return the size of the output buffer required for an update
+     * an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to update
+     * with len bytes of input.
+     */
+    public int getUpdateOutputSize(
+        int len)
+    {
+        int total       = len + bufOff;
+        int leftOver    = total % buf.length;
+
+        if (leftOver == 0)
+        {
+            return total - buf.length;
+        }
+
+        return total - leftOver;
+    }
+
+    /**
+     * return the size of the output buffer required for an update plus a
+     * doFinal with an input of len bytes.
+     *
+     * @param len the length of the input.
+     * @return the space required to accommodate a call to update and doFinal
+     * with len bytes of input.
+     */
+    public int getOutputSize(
+        int len)
+    {
+        return len + bufOff;
+    }
+
+    /**
+     * process a single byte, producing an output block if necessary.
+     *
+     * @param in the input byte.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception org.bouncycastle.crypto.DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processByte(
+        byte        in,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        int         resultLen = 0;
+
+        if (bufOff == buf.length)
+        {
+            resultLen = cipher.processBlock(buf, 0, out, outOff);
+            System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+            bufOff = blockSize;
+        }
+
+        buf[bufOff++] = in;
+
+        return resultLen;
+    }
+
+    /**
+     * process an array of bytes, producing output if necessary.
+     *
+     * @param in the input byte array.
+     * @param inOff the offset at which the input data starts.
+     * @param len the number of bytes to be copied out of the input array.
+     * @param out the space for any output that might be produced.
+     * @param outOff the offset from which the output will be copied.
+     * @return the number of output bytes copied to out.
+     * @exception org.bouncycastle.crypto.DataLengthException if there isn't enough space in out.
+     * @exception IllegalStateException if the cipher isn't initialised.
+     */
+    public int processBytes(
+        byte[]      in,
+        int         inOff,
+        int         len,
+        byte[]      out,
+        int         outOff)
+        throws DataLengthException, IllegalStateException
+    {
+        if (len < 0)
+        {
+            throw new IllegalArgumentException("Can't have a negative input length!");
+        }
+
+        int blockSize   = getBlockSize();
+        int length      = getUpdateOutputSize(len);
+
+        if (length > 0)
+        {
+            if ((outOff + length) > out.length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+        }
+
+        int resultLen = 0;
+        int gapLen = buf.length - bufOff;
+
+        if (len > gapLen)
+        {
+            System.arraycopy(in, inOff, buf, bufOff, gapLen);
+
+            resultLen += cipher.processBlock(buf, 0, out, outOff);
+            System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+            bufOff = blockSize;
+
+            len -= gapLen;
+            inOff += gapLen;
+
+            while (len > blockSize)
+            {
+                System.arraycopy(in, inOff, buf, bufOff, blockSize);
+                resultLen += cipher.processBlock(buf, 0, out, outOff + resultLen);
+                System.arraycopy(buf, blockSize, buf, 0, blockSize);
+
+                len -= blockSize;
+                inOff += blockSize;
+            }
+        }
+
+        System.arraycopy(in, inOff, buf, bufOff, len);
+
+        bufOff += len;
+
+        return resultLen;
+    }
+
+    /**
+     * Process the last block in the buffer.
+     *
+     * @param out the array the block currently being held is copied into.
+     * @param outOff the offset at which the copying starts.
+     * @return the number of output bytes copied to out.
+     * @exception org.bouncycastle.crypto.DataLengthException if there is insufficient space in out for
+     * the output.
+     * @exception IllegalStateException if the underlying cipher is not
+     * initialised.
+     * @exception org.bouncycastle.crypto.InvalidCipherTextException if cipher text decrypts wrongly (in
+     * case the exception will never get thrown).
+     */
+    public int doFinal(
+        byte[]  out,
+        int     outOff)
+        throws DataLengthException, IllegalStateException, InvalidCipherTextException
+    {
+        if (bufOff + outOff > out.length)
+        {
+            throw new DataLengthException("output buffer to small in doFinal");
+        }
+
+        int     blockSize = cipher.getBlockSize();
+        int     len = bufOff - blockSize;
+        byte[]  block = new byte[blockSize];
+
+        if (forEncryption)
+        {
+            cipher.processBlock(buf, 0, block, 0);
+            
+            if (bufOff < blockSize)
+            {
+                throw new DataLengthException("need at least one block of input for CTS");
+            }
+
+            for (int i = bufOff; i != buf.length; i++)
+            {
+                buf[i] = block[i - blockSize];
+            }
+
+            for (int i = blockSize; i != bufOff; i++)
+            {
+                buf[i] ^= block[i - blockSize];
+            }
+
+            if (cipher instanceof CBCBlockCipher)
+            {
+                BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+                c.processBlock(buf, blockSize, out, outOff);
+            }
+            else
+            {
+                cipher.processBlock(buf, blockSize, out, outOff);
+            }
+
+            System.arraycopy(block, 0, out, outOff + blockSize, len);
+        }
+        else
+        {
+            byte[]  lastBlock = new byte[blockSize];
+
+            if (cipher instanceof CBCBlockCipher)
+            {
+                BlockCipher c = ((CBCBlockCipher)cipher).getUnderlyingCipher();
+
+                c.processBlock(buf, 0, block, 0);
+            }
+            else
+            {
+                cipher.processBlock(buf, 0, block, 0);
+            }
+
+            for (int i = blockSize; i != bufOff; i++)
+            {
+                lastBlock[i - blockSize] = (byte)(block[i - blockSize] ^ buf[i]);
+            }
+
+            System.arraycopy(buf, blockSize, block, 0, len);
+
+            cipher.processBlock(block, 0, out, outOff);
+            System.arraycopy(lastBlock, 0, out, outOff + blockSize, len);
+        }
+
+        int offset = bufOff;
+
+        reset();
+
+        return offset;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
index 18e612b..4dee63a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/PGPCFBBlockCipher.java
@@ -220,13 +220,13 @@
             throw new DataLengthException("input buffer too short");
         }
 
-        if ((outOff + blockSize) > out.length)
-        {
-            throw new DataLengthException("output buffer too short");
-        }
-        
         if (count == 0)
         {
+            if ((outOff + 2 * blockSize + 2) > out.length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+
             cipher.processBlock(FR, 0, FRE, 0);
 
             for (int n = 0; n < blockSize; n++) 
@@ -258,6 +258,11 @@
         }
         else if (count >= blockSize + 2)
         {
+            if ((outOff + blockSize) > out.length)
+            {
+                throw new DataLengthException("output buffer too short");
+            }
+
             cipher.processBlock(FR, 0, FRE, 0);
 
             for (int n = 0; n < blockSize; n++) 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
index f2be2fc..fc25810 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/BasicGCMExponentiator.java
@@ -4,21 +4,21 @@
 
 public class BasicGCMExponentiator implements GCMExponentiator
 {
-    private byte[] x;
+    private int[] x;
 
     public void init(byte[] x)
     {
-        this.x = Arrays.clone(x);
+        this.x = GCMUtil.asInts(x);
     }
 
     public void exponentiateX(long pow, byte[] output)
     {
         // Initial value is little-endian 1
-        byte[] y = GCMUtil.oneAsBytes();
+        int[] y = GCMUtil.oneAsInts();
 
         if (pow > 0)
         {
-            byte[] powX = Arrays.clone(x);
+            int[] powX = Arrays.clone(x);
             do
             {
                 if ((pow & 1L) != 0)
@@ -31,6 +31,6 @@
             while (pow > 0);
         }
 
-        System.arraycopy(y, 0, output, 0, 16);
+        GCMUtil.asBytes(y, output);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
index 4875301..3031a44 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/GCMUtil.java
@@ -5,6 +5,32 @@
 
 abstract class GCMUtil
 {
+    private static final int E1 = 0xe1000000;
+    private static final byte E1B = (byte)0xe1;
+    private static final long E1L = (E1 & 0xFFFFFFFFL) << 24;
+
+    private static int[] generateLookup()
+    {
+        int[] lookup = new int[256];
+
+        for (int c = 0; c < 256; ++c)
+        {
+            int v = 0;
+            for (int i = 7; i >= 0; --i)
+            {
+                if ((c & (1 << i)) != 0)
+                {
+                    v ^= (E1 >>> (7 - i));
+                }
+            }
+            lookup[c] = v;
+        }
+
+        return lookup;
+    }
+
+    private static final int[] LOOKUP = generateLookup();
+
     static byte[] oneAsBytes()
     {
         byte[] tmp = new byte[16];
@@ -15,78 +41,155 @@
     static int[] oneAsInts()
     {
         int[] tmp = new int[4];
-        tmp[0] = 0x80000000;
+        tmp[0] = 1 << 31;
         return tmp;
     }
 
-    static byte[] asBytes(int[] ns)
+    static long[] oneAsLongs()
     {
-        byte[] output = new byte[16];
-        Pack.intToBigEndian(ns, output, 0);
-        return output;
+        long[] tmp = new long[2];
+        tmp[0] = 1L << 63;
+        return tmp;
     }
 
-    static int[] asInts(byte[] bs)
+    static byte[] asBytes(int[] x)
     {
-        int[] output = new int[4];
-        Pack.bigEndianToInt(bs, 0, output);
-        return output;
+        byte[] z = new byte[16];
+        Pack.intToBigEndian(x, z, 0);
+        return z;
     }
 
-    static void asInts(byte[] bs, int[] output)
+    static void asBytes(int[] x, byte[] z)
     {
-        Pack.bigEndianToInt(bs, 0, output);
+        Pack.intToBigEndian(x, z, 0);
     }
 
-    static void multiply(byte[] block, byte[] val)
+    static byte[] asBytes(long[] x)
     {
-        byte[] tmp = Arrays.clone(block);
-        byte[] c = new byte[16];
+        byte[] z = new byte[16];
+        Pack.longToBigEndian(x, z, 0);
+        return z;
+    }
+
+    static void asBytes(long[] x, byte[] z)
+    {
+        Pack.longToBigEndian(x, z, 0);
+    }
+
+    static int[] asInts(byte[] x)
+    {
+        int[] z = new int[4];
+        Pack.bigEndianToInt(x, 0, z);
+        return z;
+    }
+
+    static void asInts(byte[] x, int[] z)
+    {
+        Pack.bigEndianToInt(x, 0, z);
+    }
+
+    static long[] asLongs(byte[] x)
+    {
+        long[] z = new long[2];
+        Pack.bigEndianToLong(x, 0, z);
+        return z;
+    }
+
+    static void asLongs(byte[] x, long[] z)
+    {
+        Pack.bigEndianToLong(x, 0, z);
+    }
+
+    static void multiply(byte[] x, byte[] y)
+    {
+        byte[] r0 = Arrays.clone(x);
+        byte[] r1 = new byte[16];
 
         for (int i = 0; i < 16; ++i)
         {
-            byte bits = val[i];
+            byte bits = y[i];
             for (int j = 7; j >= 0; --j)
             {
                 if ((bits & (1 << j)) != 0)
                 {
-                    xor(c, tmp);
+                    xor(r1, r0);
                 }
 
-                boolean lsb = (tmp[15] & 1) != 0;
-                shiftRight(tmp);
-                if (lsb)
+                if (shiftRight(r0) != 0)
                 {
-                    // R = new byte[]{ 0xe1, ... };
-//                    GCMUtil.xor(v, R);
-                    tmp[0] ^= (byte)0xe1;
+                    r0[0] ^= E1B;
                 }
             }
         }
 
-        System.arraycopy(c, 0, block, 0, 16);
+        System.arraycopy(r1, 0, x, 0, 16);
+    }
+
+    static void multiply(int[] x, int[] y)
+    {
+        int[] r0 = Arrays.clone(x);
+        int[] r1 = new int[4];
+
+        for (int i = 0; i < 4; ++i)
+        {
+            int bits = y[i];
+            for (int j = 31; j >= 0; --j)
+            {
+                if ((bits & (1 << j)) != 0)
+                {
+                    xor(r1, r0);
+                }
+
+                if (shiftRight(r0) != 0)
+                {
+                    r0[0] ^= E1;
+                }
+            }
+        }
+
+        System.arraycopy(r1, 0, x, 0, 4);
+    }
+
+    static void multiply(long[] x, long[] y)
+    {
+        long[] r0 = new long[]{ x[0], x[1] };
+        long[] r1 = new long[2];
+
+        for (int i = 0; i < 2; ++i)
+        {
+            long bits = y[i];
+            for (int j = 63; j >= 0; --j)
+            {
+                if ((bits & (1L << j)) != 0)
+                {
+                    xor(r1, r0);
+                }
+
+                if (shiftRight(r0) != 0)
+                {
+                    r0[0] ^= E1L;
+                }
+            }
+        }
+
+        x[0] = r1[0];
+        x[1] = r1[1];
     }
 
     // P is the value with only bit i=1 set
     static void multiplyP(int[] x)
     {
-        boolean lsb = (x[3] & 1) != 0;
-        shiftRight(x);
-        if (lsb)
+        if (shiftRight(x) != 0)
         {
-            // R = new int[]{ 0xe1000000, 0, 0, 0 };
-//            xor(v, R);
-            x[0] ^= 0xe1000000;
+            x[0] ^= E1;
         }
     }
 
-    static void multiplyP(int[] x, int[] output)
+    static void multiplyP(int[] x, int[] y)
     {
-        boolean lsb = (x[3] & 1) != 0;
-        shiftRight(x, output);
-        if (lsb)
+        if (shiftRight(x, y) != 0)
         {
-            output[0] ^= 0xe1000000;
+            y[0] ^= E1;
         }
     }
 
@@ -98,163 +201,257 @@
 //            multiplyP(x);
 //        }
 
-        int lsw = x[3];
-        shiftRightN(x, 8);
-        for (int i = 7; i >= 0; --i)
-        {
-            if ((lsw & (1 << i)) != 0)
-            {
-                x[0] ^= (0xe1000000 >>> (7 - i));
-            }
-        }
+        int c = shiftRightN(x, 8);
+        x[0] ^= LOOKUP[c >>> 24];
     }
 
-    static void multiplyP8(int[] x, int[] output)
+    static void multiplyP8(int[] x, int[] y)
     {
-        int lsw = x[3];
-        shiftRightN(x, 8, output);
-        for (int i = 7; i >= 0; --i)
-        {
-            if ((lsw & (1 << i)) != 0)
-            {
-                output[0] ^= (0xe1000000 >>> (7 - i));
-            }
-        }
+        int c = shiftRightN(x, 8, y);
+        y[0] ^= LOOKUP[c >>> 24];
     }
 
-    static void shiftRight(byte[] block)
+    static byte shiftRight(byte[] x)
+    {
+//        int c = 0;
+//        for (int i = 0; i < 16; ++i)
+//        {
+//            int b = x[i] & 0xff;
+//            x[i] = (byte)((b >>> 1) | c);
+//            c = (b & 1) << 7;
+//        }
+//        return (byte)c;
+
+        int i = 0, c = 0;
+        do
+        {
+            int b = x[i] & 0xff;
+            x[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+            b = x[i] & 0xff;
+            x[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+            b = x[i] & 0xff;
+            x[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+            b = x[i] & 0xff;
+            x[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+        }
+        while (i < 16);
+        return (byte)c;
+    }
+
+    static byte shiftRight(byte[] x, byte[] z)
+    {
+//        int c = 0;
+//        for (int i = 0; i < 16; ++i)
+//        {
+//            int b = x[i] & 0xff;
+//            z[i] = (byte) ((b >>> 1) | c);
+//            c = (b & 1) << 7;
+//        }
+//        return (byte) c;
+
+        int i = 0, c = 0;
+        do
+        {
+            int b = x[i] & 0xff;
+            z[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+            b = x[i] & 0xff;
+            z[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+            b = x[i] & 0xff;
+            z[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+            b = x[i] & 0xff;
+            z[i++] = (byte)((b >>> 1) | c);
+            c = (b & 1) << 7;
+        }
+        while (i < 16);
+        return (byte)c;
+    }
+
+    static int shiftRight(int[] x)
+    {
+//        int c = 0;
+//        for (int i = 0; i < 4; ++i)
+//        {
+//            int b = x[i];
+//            x[i] = (b >>> 1) | c;
+//            c = b << 31;
+//        }
+//        return c;
+
+        int b = x[0];
+        x[0] = b >>> 1;
+        int c = b << 31;
+        b = x[1];
+        x[1] = (b >>> 1) | c;
+        c = b << 31;
+        b = x[2];
+        x[2] = (b >>> 1) | c;
+        c = b << 31;
+        b = x[3];
+        x[3] = (b >>> 1) | c;
+        return b << 31;
+    }
+
+    static int shiftRight(int[] x, int[] z)
+    {
+//      int c = 0;
+//      for (int i = 0; i < 4; ++i)
+//      {
+//          int b = x[i];
+//          z[i] = (b >>> 1) | c;
+//          c = b << 31;
+//      }
+//      return c;
+
+        int b = x[0];
+        z[0] = b >>> 1;
+        int c = b << 31;
+        b = x[1];
+        z[1] = (b >>> 1) | c;
+        c = b << 31;
+        b = x[2];
+        z[2] = (b >>> 1) | c;
+        c = b << 31;
+        b = x[3];
+        z[3] = (b >>> 1) | c;
+        return b << 31;
+    }
+
+    static long shiftRight(long[] x)
+    {
+        long b = x[0];
+        x[0] = b >>> 1;
+        long c = b << 63; 
+        b = x[1];
+        x[1] = (b >>> 1) | c;
+        return b << 63;
+    }
+
+    static long shiftRight(long[] x, long[] z)
+    {
+        long b = x[0];
+        z[0] = b >>> 1;
+        long c = b << 63; 
+        b = x[1];
+        z[1] = (b >>> 1) | c;
+        return b << 63;
+    }
+
+    static int shiftRightN(int[] x, int n)
+    {
+//        int c = 0, nInv = 32 - n;
+//        for (int i = 0; i < 4; ++i)
+//        {
+//            int b = x[i];
+//            x[i] = (b >>> n) | c;
+//            c = b << nInv;
+//        }
+//        return c;
+
+        int b = x[0], nInv = 32 - n;
+        x[0] = b >>> n;
+        int c = b << nInv;
+        b = x[1];
+        x[1] = (b >>> n) | c;
+        c = b << nInv;
+        b = x[2];
+        x[2] = (b >>> n) | c;
+        c = b << nInv;
+        b = x[3];
+        x[3] = (b >>> n) | c;
+        return b << nInv;
+    }
+
+    static int shiftRightN(int[] x, int n, int[] z)
+    {
+//        int c = 0, nInv = 32 - n;
+//        for (int i = 0; i < 4; ++i)
+//        {
+//            int b = x[i];
+//            z[i] = (b >>> n) | c;
+//            c = b << nInv;
+//        }
+//        return c;
+
+        int b = x[0], nInv = 32 - n;
+        z[0] = b >>> n;
+        int c = b << nInv;
+        b = x[1];
+        z[1] = (b >>> n) | c;
+        c = b << nInv;
+        b = x[2];
+        z[2] = (b >>> n) | c;
+        c = b << nInv;
+        b = x[3];
+        z[3] = (b >>> n) | c;
+        return b << nInv;
+    }
+
+    static void xor(byte[] x, byte[] y)
     {
         int i = 0;
-        int bit = 0;
-        for (;;)
+        do
         {
-            int b = block[i] & 0xff;
-            block[i] = (byte) ((b >>> 1) | bit);
-            if (++i == 16)
-            {
-                break;
-            }
-            bit = (b & 1) << 7;
+            x[i] ^= y[i]; ++i;
+            x[i] ^= y[i]; ++i;
+            x[i] ^= y[i]; ++i;
+            x[i] ^= y[i]; ++i;
+        }
+        while (i < 16);
+    }
+
+    static void xor(byte[] x, byte[] y, int yOff, int yLen)
+    {
+        while (yLen-- > 0)
+        {
+            x[yLen] ^= y[yOff + yLen];
         }
     }
 
-    static void shiftRight(byte[] block, byte[] output)
+    static void xor(byte[] x, byte[] y, byte[] z)
     {
         int i = 0;
-        int bit = 0;
-        for (;;)
+        do
         {
-            int b = block[i] & 0xff;
-            output[i] = (byte) ((b >>> 1) | bit);
-            if (++i == 16)
-            {
-                break;
-            }
-            bit = (b & 1) << 7;
+            z[i] = (byte)(x[i] ^ y[i]); ++i;
+            z[i] = (byte)(x[i] ^ y[i]); ++i;
+            z[i] = (byte)(x[i] ^ y[i]); ++i;
+            z[i] = (byte)(x[i] ^ y[i]); ++i;
         }
+        while (i < 16);
     }
 
-    static void shiftRight(int[] block)
+    static void xor(int[] x, int[] y)
     {
-        int i = 0;
-        int bit = 0;
-        for (;;)
-        {
-            int b = block[i];
-            block[i] = (b >>> 1) | bit;
-            if (++i == 4)
-            {
-                break;
-            }
-            bit = b << 31;
-        }
+        x[0] ^= y[0];
+        x[1] ^= y[1];
+        x[2] ^= y[2];
+        x[3] ^= y[3];
     }
 
-    static void shiftRight(int[] block, int[] output)
+    static void xor(int[] x, int[] y, int[] z)
     {
-        int i = 0;
-        int bit = 0;
-        for (;;)
-        {
-            int b = block[i];
-            output[i] = (b >>> 1) | bit;
-            if (++i == 4)
-            {
-                break;
-            }
-            bit = b << 31;
-        }
+        z[0] = x[0] ^ y[0];
+        z[1] = x[1] ^ y[1];
+        z[2] = x[2] ^ y[2];
+        z[3] = x[3] ^ y[3];
     }
 
-    static void shiftRightN(int[] block, int n)
+    static void xor(long[] x, long[] y)
     {
-        int i = 0;
-        int bits = 0;
-        for (;;)
-        {
-            int b = block[i];
-            block[i] = (b >>> n) | bits;
-            if (++i == 4)
-            {
-                break;
-            }
-            bits = b << (32 - n);
-        }
+        x[0] ^= y[0];
+        x[1] ^= y[1];
     }
 
-    static void shiftRightN(int[] block, int n, int[] output)
+    static void xor(long[] x, long[] y, long[] z)
     {
-        int i = 0;
-        int bits = 0;
-        for (;;)
-        {
-            int b = block[i];
-            output[i] = (b >>> n) | bits;
-            if (++i == 4)
-            {
-                break;
-            }
-            bits = b << (32 - n);
-        }
-    }
-
-    static void xor(byte[] block, byte[] val)
-    {
-        for (int i = 15; i >= 0; --i)
-        {
-            block[i] ^= val[i];
-        }
-    }
-
-    static void xor(byte[] block, byte[] val, int off, int len)
-    {
-        while (len-- > 0)
-        {
-            block[len] ^= val[off + len];
-        }
-    }
-
-    static void xor(byte[] block, byte[] val, byte[] output)
-    {
-        for (int i = 15; i >= 0; --i)
-        {
-            output[i] = (byte)(block[i] ^ val[i]);
-        }
-    }
-
-    static void xor(int[] block, int[] val)
-    {
-        for (int i = 3; i >= 0; --i)
-        {
-            block[i] ^= val[i];
-        }
-    }
-
-    static void xor(int[] block, int[] val, int[] output)
-    {
-        for (int i = 3; i >= 0; --i)
-        {
-            output[i] = block[i] ^ val[i];
-        }
+        z[0] = x[0] ^ y[0];
+        z[1] = x[1] ^ y[1];
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
index a051208..6eff4e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/modes/gcm/Tables1kGCMExponentiator.java
@@ -12,31 +12,32 @@
 
     public void init(byte[] x)
     {
-        if (lookupPowX2 != null && Arrays.areEqual(x, (byte[])lookupPowX2.elementAt(0)))
+        int[] y = GCMUtil.asInts(x);
+        if (lookupPowX2 != null && Arrays.areEqual(y, (int[])lookupPowX2.elementAt(0)))
         {
             return;
         }
 
         lookupPowX2 = new Vector(8);
-        lookupPowX2.addElement(Arrays.clone(x));
+        lookupPowX2.addElement(y);
     }
 
     public void exponentiateX(long pow, byte[] output)
     {
-        byte[] y = GCMUtil.oneAsBytes();
+        int[] y = GCMUtil.oneAsInts();
         int bit = 0;
         while (pow > 0)
         {
             if ((pow & 1L) != 0)
             {
                 ensureAvailable(bit);
-                GCMUtil.multiply(y, (byte[])lookupPowX2.elementAt(bit));
+                GCMUtil.multiply(y, (int[])lookupPowX2.elementAt(bit));
             }
             ++bit;
             pow >>>= 1;
         }
 
-        System.arraycopy(y, 0, output, 0, 16);
+        GCMUtil.asBytes(y, output);
     }
 
     private void ensureAvailable(int bit)
@@ -44,7 +45,7 @@
         int count = lookupPowX2.size();
         if (count <= bit)
         {
-            byte[] tmp = (byte[])lookupPowX2.elementAt(count - 1);
+            int[] tmp = (int[])lookupPowX2.elementAt(count - 1);
             do
             {
                 tmp = Arrays.clone(tmp);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html
deleted file mode 100644
index 5402df4..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/modes/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Modes for symmetric ciphers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/package.html
deleted file mode 100644
index ee5487f..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Base classes for the lightweight API.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html
deleted file mode 100644
index 2b82e60..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/paddings/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Paddings for symmetric ciphers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
index 05a1327..9cc6e72 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECDomainParameters.java
@@ -41,7 +41,7 @@
         byte[]      seed)
     {
         this.curve = curve;
-        this.G = G;
+        this.G = G.normalize();
         this.n = n;
         this.h = h;
         this.seed = seed;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
index 5fbea19..b6b3fb6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/ECPublicKeyParameters.java
@@ -12,7 +12,7 @@
         ECDomainParameters  params)
     {
         super(false, params);
-        this.Q = Q;
+        this.Q = Q.normalize();
     }
 
     public ECPoint getQ()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
new file mode 100644
index 0000000..0eb6cb7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFCounterParameters.java
@@ -0,0 +1,52 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+public final class KDFCounterParameters
+    implements DerivationParameters
+{
+
+    private final byte[] ki;
+    private final byte[] fixedInputData;
+    private final int r;
+
+    public KDFCounterParameters(byte[] ki, byte[] fixedInputData, int r)
+    {
+        if (ki == null)
+        {
+            throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+        }
+        this.ki = Arrays.clone(ki);
+
+        if (fixedInputData == null)
+        {
+            this.fixedInputData = new byte[0];
+        }
+        else
+        {
+            this.fixedInputData = Arrays.clone(fixedInputData);
+        }
+
+        if (r != 8 && r != 16 && r != 24 && r != 32)
+        {
+            throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+        }
+        this.r = r;
+    }
+
+    public byte[] getKI()
+    {
+        return ki;
+    }
+
+    public byte[] getFixedInputData()
+    {
+        return Arrays.clone(fixedInputData);
+    }
+
+    public int getR()
+    {
+        return r;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java
new file mode 100644
index 0000000..383678a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFDoublePipelineIterationParameters.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Note that counter is only supported at the location presented in the
+ * NIST SP 800-108 specification, not in the additional locations present
+ * in the CAVP test vectors.
+ */
+public final class KDFDoublePipelineIterationParameters
+    implements DerivationParameters
+{
+
+    // could be any valid value, using 32, don't know why
+    private static final int UNUSED_R = 32;
+
+    private final byte[] ki;
+    private final boolean useCounter;
+    private final int r;
+    private final byte[] fixedInputData;
+
+    private KDFDoublePipelineIterationParameters(byte[] ki, byte[] fixedInputData, int r, boolean useCounter)
+    {
+        if (ki == null)
+        {
+            throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+        }
+        this.ki = Arrays.clone(ki);
+
+        if (fixedInputData == null)
+        {
+            this.fixedInputData = new byte[0];
+        }
+        else
+        {
+            this.fixedInputData = Arrays.clone(fixedInputData);
+        }
+
+        if (r != 8 && r != 16 && r != 24 && r != 32)
+        {
+            throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+        }
+        this.r = r;
+
+        this.useCounter = useCounter;
+    }
+
+    public static KDFDoublePipelineIterationParameters createWithCounter(
+        byte[] ki, byte[] fixedInputData, int r)
+    {
+        return new KDFDoublePipelineIterationParameters(ki, fixedInputData, r, true);
+    }
+
+    public static KDFDoublePipelineIterationParameters createWithoutCounter(
+        byte[] ki, byte[] fixedInputData)
+    {
+        return new KDFDoublePipelineIterationParameters(ki, fixedInputData, UNUSED_R, false);
+    }
+
+    public byte[] getKI()
+    {
+        return ki;
+    }
+
+    public boolean useCounter()
+    {
+        return useCounter;
+    }
+
+    public int getR()
+    {
+        return r;
+    }
+
+    public byte[] getFixedInputData()
+    {
+        return Arrays.clone(fixedInputData);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java
new file mode 100644
index 0000000..8a6e7f5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/KDFFeedbackParameters.java
@@ -0,0 +1,96 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.DerivationParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Note that counter is only supported at the location presented in the
+ * NIST SP 800-108 specification, not in the additional locations present
+ * in the CAVP test vectors.
+ */
+public final class KDFFeedbackParameters
+    implements DerivationParameters
+{
+
+    // could be any valid value, using 32, don't know why
+    private static final int UNUSED_R = -1;
+
+    private final byte[] ki;
+    private final byte[] iv;
+    private final boolean useCounter;
+    private final int r;
+    private final byte[] fixedInputData;
+
+    private KDFFeedbackParameters(byte[] ki, byte[] iv, byte[] fixedInputData, int r, boolean useCounter)
+    {
+        if (ki == null)
+        {
+            throw new IllegalArgumentException("A KDF requires Ki (a seed) as input");
+        }
+        this.ki = Arrays.clone(ki);
+
+        if (fixedInputData == null)
+        {
+            this.fixedInputData = new byte[0];
+        }
+        else
+        {
+            this.fixedInputData = Arrays.clone(fixedInputData);
+        }
+
+        this.r = r;
+
+        if (iv == null)
+        {
+            this.iv = new byte[0];
+        }
+        else
+        {
+            this.iv = Arrays.clone(iv);
+        }
+
+        this.useCounter = useCounter;
+    }
+
+    public static KDFFeedbackParameters createWithCounter(
+        byte[] ki, final byte[] iv, byte[] fixedInputData, int r)
+    {
+        if (r != 8 && r != 16 && r != 24 && r != 32)
+        {
+            throw new IllegalArgumentException("Length of counter should be 8, 16, 24 or 32");
+        }
+
+        return new KDFFeedbackParameters(ki, iv, fixedInputData, r, true);
+    }
+
+    public static KDFFeedbackParameters createWithoutCounter(
+        byte[] ki, final byte[] iv, byte[] fixedInputData)
+    {
+        return new KDFFeedbackParameters(ki, iv, fixedInputData, UNUSED_R, false);
+    }
+
+    public byte[] getKI()
+    {
+        return ki;
+    }
+
+    public byte[] getIV()
+    {
+        return iv;
+    }
+
+    public boolean useCounter()
+    {
+        return useCounter;
+    }
+
+    public int getR()
+    {
+        return r;
+    }
+
+    public byte[] getFixedInputData()
+    {
+        return Arrays.clone(fixedInputData);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
new file mode 100644
index 0000000..76241ee
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/SkeinParameters.java
@@ -0,0 +1,293 @@
+package org.bouncycastle.crypto.params;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.crypto.digests.SkeinEngine;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.util.Integers;
+
+/**
+ * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+ * <p/>
+ * Parameterised Skein can be used for:
+ * <ul>
+ * <li>MAC generation, by providing a {@link SkeinParameters.Builder#setKey(byte[]) key}.</li>
+ * <li>Randomised hashing, by providing a {@link SkeinParameters.Builder#setNonce(byte[]) nonce}.</li>
+ * <li>A hash function for digital signatures, associating a
+ * {@link SkeinParameters.Builder#setPublicKey(byte[]) public key} with the message digest.</li>
+ * <li>A key derivation function, by providing a
+ * {@link SkeinParameters.Builder#setKeyIdentifier(byte[]) key identifier}.</li>
+ * <li>Personalised hashing, by providing a
+ * {@link SkeinParameters.Builder#setPersonalisation(Date, String, String) recommended format} or
+ * {@link SkeinParameters.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li>
+ * </ul>
+ *
+ * @see SkeinEngine
+ * @see SkeinDigest
+ * @see SkeinMac
+ */
+public class SkeinParameters
+    implements CipherParameters
+{
+    /**
+     * The parameter type for a secret key, supporting MAC or KDF functions: {@value
+     * #PARAM_TYPE_KEY}.
+     */
+    public static final int PARAM_TYPE_KEY = 0;
+
+    /**
+     * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}.
+     */
+    public static final int PARAM_TYPE_CONFIG = 4;
+
+    /**
+     * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}.
+     */
+    public static final int PARAM_TYPE_PERSONALISATION = 8;
+
+    /**
+     * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}.
+     */
+    public static final int PARAM_TYPE_PUBLIC_KEY = 12;
+
+    /**
+     * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}.
+     */
+    public static final int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+    /**
+     * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}.
+     */
+    public static final int PARAM_TYPE_NONCE = 20;
+
+    /**
+     * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}.
+     */
+    public static final int PARAM_TYPE_MESSAGE = 48;
+
+    /**
+     * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}.
+     */
+    public static final int PARAM_TYPE_OUTPUT = 63;
+
+    private Hashtable parameters;
+
+    public SkeinParameters()
+    {
+        this(new Hashtable());
+    }
+
+    private SkeinParameters(final Hashtable parameters)
+    {
+        this.parameters = parameters;
+    }
+
+    /**
+     * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object.
+     */
+    public Hashtable getParameters()
+    {
+        return parameters;
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not
+     * set.
+     */
+    public byte[] getKey()
+    {
+        return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+     * <code>null</code> if not set.
+     */
+    public byte[] getPersonalisation()
+    {
+        return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+     * <code>null</code> if not set.
+     */
+    public byte[] getPublicKey()
+    {
+        return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+     * <code>null</code> if not set.
+     */
+    public byte[] getKeyIdentifier()
+    {
+        return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if
+     * not set.
+     */
+    public byte[] getNonce()
+    {
+        return (byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE));
+    }
+
+    /**
+     * A builder for {@link SkeinParameters}.
+     */
+    public static class Builder
+    {
+        private Hashtable parameters = new Hashtable();
+
+        public Builder()
+        {
+        }
+
+        public Builder(Hashtable paramsMap)
+        {
+            Enumeration keys = paramsMap.keys();
+            while (keys.hasMoreElements())
+            {
+                Integer key = (Integer)keys.nextElement();
+                parameters.put(key, paramsMap.get(key));
+            }
+        }
+
+        public Builder(SkeinParameters params)
+        {
+            Enumeration keys = params.parameters.keys();
+            while (keys.hasMoreElements())
+            {
+                Integer key = (Integer)keys.nextElement();
+                parameters.put(key, params.parameters.get(key));
+            }
+        }
+
+        /**
+         * Sets a parameters to apply to the Skein hash function.<br>
+         * Parameter types must be in the range 0,5..62, and cannot use the value {@value
+         * SkeinParameters#PARAM_TYPE_MESSAGE} (reserved for message body).
+         * <p/>
+         * Parameters with type < {@value SkeinParameters#PARAM_TYPE_MESSAGE} are processed before
+         * the message content, parameters with type > {@value SkeinParameters#PARAM_TYPE_MESSAGE}
+         * are processed after the message and prior to output.
+         *
+         * @param type  the type of the parameter, in the range 5..62.
+         * @param value the byte sequence of the parameter.
+         * @return
+         */
+        public Builder set(int type, byte[] value)
+        {
+            if (value == null)
+            {
+                throw new IllegalArgumentException("Parameter value must not be null.");
+            }
+            if ((type != PARAM_TYPE_KEY)
+                && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+            {
+                throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+            }
+            if (type == PARAM_TYPE_CONFIG)
+            {
+                throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+                    + " is reserved for internal use.");
+            }
+            this.parameters.put(Integers.valueOf(type), value);
+            return this;
+        }
+
+        /**
+         * Sets the {@link SkeinParameters#PARAM_TYPE_KEY} parameter.
+         */
+        public Builder setKey(byte[] key)
+        {
+            return set(PARAM_TYPE_KEY, key);
+        }
+
+        /**
+         * Sets the {@link SkeinParameters#PARAM_TYPE_PERSONALISATION} parameter.
+         */
+        public Builder setPersonalisation(byte[] personalisation)
+        {
+            return set(PARAM_TYPE_PERSONALISATION, personalisation);
+        }
+
+        /**
+         * Implements the recommended personalisation format for Skein defined in Section 4.11 of
+         * the Skein 1.3 specification.
+         * <p/>
+         * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte
+         * sequence using UTF-8 encoding.
+         *
+         * @param date          the date the personalised application of the Skein was defined.
+         * @param emailAddress  the email address of the creation of the personalised application.
+         * @param distinguisher an arbitrary personalisation string distinguishing the application.
+         * @return
+         */
+        public Builder setPersonalisation(Date date, String emailAddress, String distinguisher)
+        {
+            try
+            {
+                final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+                final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+                final DateFormat format = new SimpleDateFormat("YYYYMMDD");
+                out.write(format.format(date));
+                out.write(" ");
+                out.write(emailAddress);
+                out.write(" ");
+                out.write(distinguisher);
+                out.close();
+                return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray());
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("Byte I/O failed: " + e);
+            }
+        }
+
+        /**
+         * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+         */
+        public Builder setPublicKey(byte[] publicKey)
+        {
+            return set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+        }
+
+        /**
+         * Sets the {@link SkeinParameters#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+         */
+        public Builder setKeyIdentifier(byte[] keyIdentifier)
+        {
+            return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+        }
+
+        /**
+         * Sets the {@link SkeinParameters#PARAM_TYPE_NONCE} parameter.
+         */
+        public Builder setNonce(byte[] nonce)
+        {
+            return set(PARAM_TYPE_NONCE, nonce);
+        }
+
+        /**
+         * Constructs a new {@link SkeinParameters} instance with the parameters provided to this
+         * builder.
+         */
+        public SkeinParameters build()
+        {
+            return new SkeinParameters(parameters);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java
new file mode 100644
index 0000000..fa16fac
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/params/TweakableBlockCipherParameters.java
@@ -0,0 +1,40 @@
+package org.bouncycastle.crypto.params;
+
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * Parameters for tweakable block ciphers.
+ */
+public class TweakableBlockCipherParameters
+    implements CipherParameters
+{
+    private final byte[] tweak;
+    private final KeyParameter key;
+
+    public TweakableBlockCipherParameters(final KeyParameter key, final byte[] tweak)
+    {
+        this.key = key;
+        this.tweak = Arrays.clone(tweak);
+    }
+
+    /**
+     * Gets the key.
+     *
+     * @return the key to use, or <code>null</code> to use the current key.
+     */
+    public KeyParameter getKey()
+    {
+        return key;
+    }
+
+    /**
+     * Gets the tweak value.
+     *
+     * @return the tweak to use, or <code>null</code> to use the current tweak.
+     */
+    public byte[] getTweak()
+    {
+        return tweak;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html
deleted file mode 100644
index 4e00a75..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/params/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for parameter objects for ciphers and generators.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
index 209b5e2..3245eab 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/FixedSecureRandom.java
@@ -4,6 +4,9 @@
 import java.io.IOException;
 import java.security.SecureRandom;
 
+/**
+ * A secure random that returns pre-seeded data to calls of nextBytes() or generateSeed().
+ */
 public class FixedSecureRandom
     extends SecureRandom
 {
@@ -70,7 +73,16 @@
         
         _index += bytes.length;
     }
-    
+
+    public byte[] generateSeed(int numBytes)
+    {
+        byte[] bytes = new byte[numBytes];
+
+        this.nextBytes(bytes);
+
+        return bytes;
+    }
+
     //
     // classpath's implementation of SecureRandom doesn't currently go back to nextBytes
     // when next is called. We can't override next as it's a final method.
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
index 66f05c5..59ea101 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/SP800SecureRandomBuilder.java
@@ -6,6 +6,7 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.prng.drbg.CTRSP800DRBG;
+import org.bouncycastle.crypto.prng.drbg.DualECPoints;
 import org.bouncycastle.crypto.prng.drbg.DualECSP800DRBG;
 import org.bouncycastle.crypto.prng.drbg.HMacSP800DRBG;
 import org.bouncycastle.crypto.prng.drbg.HashSP800DRBG;
@@ -144,7 +145,7 @@
     }
 
     /**
-     * Build a SecureRandom based on a SP 800-90A Dual EC DRBG.
+     * Build a SecureRandom based on a SP 800-90A Dual EC DRBG using the NIST point set.
      *
      * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
      * @param nonce  nonce value to use in DRBG construction.
@@ -156,6 +157,21 @@
         return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new DualECDRBGProvider(digest, nonce, personalizationString, securityStrength), predictionResistant);
     }
 
+    /**
+     * Build a SecureRandom based on a SP 800-90A Dual EC DRBG according to a defined point set.
+     *
+     * @param pointSet an array of DualECPoints to use for DRB generation.
+     * @param digest digest algorithm to use in the DRBG underneath the SecureRandom.
+     * @param nonce  nonce value to use in DRBG construction.
+     * @param predictionResistant specify whether the underlying DRBG in the resulting SecureRandom should reseed on each request for bytes.
+     * @return a SecureRandom supported by a Dual EC DRBG.
+     */
+    public SP800SecureRandom buildDualEC(DualECPoints[] pointSet, Digest digest, byte[] nonce, boolean predictionResistant)
+    {
+        return new SP800SecureRandom(random, entropySourceProvider.get(entropyBitsRequired), new ConfigurableDualECDRBGProvider(pointSet, digest, nonce, personalizationString, securityStrength), predictionResistant);
+    }
+
+
     private static class HashDRBGProvider
         implements DRBGProvider
     {
@@ -200,6 +216,31 @@
         }
     }
 
+    private static class ConfigurableDualECDRBGProvider
+        implements DRBGProvider
+    {
+        private final DualECPoints[] pointSet;
+        private final Digest digest;
+        private final byte[] nonce;
+        private final byte[] personalizationString;
+        private final int securityStrength;
+
+        public ConfigurableDualECDRBGProvider(DualECPoints[] pointSet, Digest digest, byte[] nonce, byte[] personalizationString, int securityStrength)
+        {
+            this.pointSet = new DualECPoints[pointSet.length];
+            System.arraycopy(pointSet, 0, this.pointSet, 0, pointSet.length);
+            this.digest = digest;
+            this.nonce = nonce;
+            this.personalizationString = personalizationString;
+            this.securityStrength = securityStrength;
+        }
+
+        public SP80090DRBG get(EntropySource entropySource)
+        {
+            return new DualECSP800DRBG(pointSet, digest, securityStrength, entropySource, personalizationString, nonce);
+        }
+    }
+
     private static class HMacDRBGProvider
         implements DRBGProvider
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java
new file mode 100644
index 0000000..c3715bc
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECPoints.java
@@ -0,0 +1,82 @@
+package org.bouncycastle.crypto.prng.drbg;
+
+import org.bouncycastle.math.ec.ECPoint;
+
+/**
+ * General class for providing point pairs for use with DualEC DRBG. See NIST SP 800-90A for further details.
+ */
+public class DualECPoints
+{
+    private final ECPoint p;
+    private final ECPoint q;
+    private final int securityStrength;
+    private final int cofactor;
+
+    /**
+     * Base Constructor.
+     * <p>
+     * The cofactor is used to calculate the output block length (maxOutlen) according to
+     * <pre>
+     *     max_outlen = largest multiple of 8 less than ((field size in bits) - (13 + log2(cofactor))
+     * </pre>
+     * </p>
+     * @param securityStrength maximum security strength to be associated with these parameters
+     * @param p the P point.
+     * @param q the Q point.
+     * @param cofactor cofactor associated with the domain parameters for the point generation.
+     */
+    public DualECPoints(int securityStrength, ECPoint p, ECPoint q, int cofactor)
+    {
+        if (!p.getCurve().equals(q.getCurve()))
+        {
+            throw new IllegalArgumentException("points need to be on the same curve");
+        }
+
+        this.securityStrength = securityStrength;
+        this.p = p;
+        this.q = q;
+        this.cofactor = cofactor;
+    }
+
+    public int getSeedLen()
+    {
+        return p.getCurve().getFieldSize();
+    }
+
+    public int getMaxOutlen()
+    {
+        return ((p.getCurve().getFieldSize() - (13 + log2(cofactor))) / 8) * 8;
+    }
+
+    public ECPoint getP()
+    {
+        return p;
+    }
+
+    public ECPoint getQ()
+    {
+        return q;
+    }
+
+    public int getSecurityStrength()
+    {
+        return securityStrength;
+    }
+
+    public int getCofactor()
+    {
+        return cofactor;
+    }
+
+    private static int log2(int value)
+    {
+        int log = 0;
+
+        while ((value >>= 1) != 0)
+        {
+            log++;
+        }
+
+        return log;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
index 3cee39c..8d326ff 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/DualECSP800DRBG.java
@@ -6,7 +6,6 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.prng.EntropySource;
 import org.bouncycastle.math.ec.ECCurve;
-import org.bouncycastle.math.ec.ECFieldElement;
 import org.bouncycastle.math.ec.ECPoint;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.BigIntegers;
@@ -35,6 +34,26 @@
     private static final BigInteger p521_Qx = new BigInteger("1b9fa3e518d683c6b65763694ac8efbaec6fab44f2276171a42726507dd08add4c3b3f4c1ebc5b1222ddba077f722943b24c3edfa0f85fe24d0c8c01591f0be6f63", 16);
     private static final BigInteger p521_Qy = new BigInteger("1f3bdba585295d9a1110d1df1f9430ef8442c5018976ff3437ef91b81dc0b8132c8d5c39c32d0e004a3092b7d327c0e7a4d26d2c7b69b58f9066652911e457779de", 16);
 
+    private static final DualECPoints[] nistPoints;
+
+    static
+    {
+        nistPoints = new DualECPoints[3];
+
+        ECCurve.Fp curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve();
+
+        nistPoints[0] = new DualECPoints(128, curve.createPoint(p256_Px, p256_Py), curve.createPoint(p256_Qx, p256_Qy), 1);
+
+        curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve();
+
+        nistPoints[1] = new DualECPoints(192, curve.createPoint(p384_Px, p384_Py), curve.createPoint(p384_Qx, p384_Qy), 1);
+
+        curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve();
+
+        nistPoints[2] = new DualECPoints(256, curve.createPoint(p521_Px, p521_Py), curve.createPoint(p521_Qx, p521_Qy), 1);
+    }
+
+
     private static final long       RESEED_MAX = 1L << (32 - 1);
     private static final int        MAX_ADDITIONAL_INPUT = 1 << (13 - 1);
     private static final int        MAX_ENTROPY_LENGTH = 1 << (13 - 1);
@@ -65,6 +84,23 @@
      */
     public DualECSP800DRBG(Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
     {
+        this(nistPoints, digest, securityStrength, entropySource, personalizationString, nonce);
+    }
+
+    /**
+     * Construct a SP800-90A Dual EC DRBG.
+     * <p>
+     * Minimum entropy requirement is the security strength requested.
+     * </p>
+     * @param pointSet an array of points to choose from, in order of increasing security strength
+     * @param digest source digest to use with the DRB stream.
+     * @param securityStrength security strength required (in bits)
+     * @param entropySource source of entropy to use for seeding/reseeding.
+     * @param personalizationString personalization string to distinguish this DRBG (may be null).
+     * @param nonce nonce to further distinguish this DRBG (may be null).
+     */
+    public DualECSP800DRBG(DualECPoints[] pointSet, Digest digest, int securityStrength, EntropySource entropySource, byte[] personalizationString, byte[] nonce)
+    {
         _digest = digest;
         _entropySource = entropySource;
         _securityStrength = securityStrength;
@@ -82,43 +118,23 @@
         byte[] entropy = entropySource.getEntropy();
         byte[] seedMaterial = Arrays.concatenate(entropy, nonce, personalizationString);
 
-        if (securityStrength <= 128)
+        for (int i = 0; i != pointSet.length; i++)
         {
-            if (Utils.getMaxSecurityStrength(digest) < 128)
+            if (securityStrength <= pointSet[i].getSecurityStrength())
             {
-                throw new IllegalArgumentException("Requested security strength is not supported by digest");
+                if (Utils.getMaxSecurityStrength(digest) < pointSet[i].getSecurityStrength())
+                {
+                    throw new IllegalArgumentException("Requested security strength is not supported by digest");
+                }
+                _seedlen = pointSet[i].getSeedLen();
+                _outlen =  pointSet[i].getMaxOutlen() / 8;
+                _P = pointSet[i].getP();
+                _Q = pointSet[i].getQ();
+                break;
             }
-            _seedlen = 256;
-            _outlen = 240 / 8;
-            _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-256").getCurve();
-            _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Px), new ECFieldElement.Fp(_curve.getQ(), p256_Py));
-            _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p256_Qx), new ECFieldElement.Fp(_curve.getQ(), p256_Qy));
         }
-        else if (securityStrength <= 192)
-        {
-            if (Utils.getMaxSecurityStrength(digest) < 192)
-            {
-                throw new IllegalArgumentException("Requested security strength is not supported by digest");
-            }
-            _seedlen = 384;
-            _outlen = 368 / 8;
-            _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-384").getCurve();
-            _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Px), new ECFieldElement.Fp(_curve.getQ(), p384_Py));
-            _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p384_Qx), new ECFieldElement.Fp(_curve.getQ(), p384_Qy));
-        }
-        else if (securityStrength <= 256)
-        {
-            if (Utils.getMaxSecurityStrength(digest) < 256)
-            {
-                throw new IllegalArgumentException("Requested security strength is not supported by digest");
-            }
-            _seedlen = 521;
-            _outlen = 504 / 8;
-            _curve = (ECCurve.Fp)NISTNamedCurves.getByName("P-521").getCurve();
-            _P = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Px), new ECFieldElement.Fp(_curve.getQ(), p521_Py));
-            _Q = new ECPoint.Fp(_curve, new ECFieldElement.Fp(_curve.getQ(), p521_Qx), new ECFieldElement.Fp(_curve.getQ(), p521_Qy));
-        }
-        else
+
+        if (_P == null)
         {
             throw new IllegalArgumentException("security strength cannot be greater than 256 bits");
         }
@@ -159,50 +175,69 @@
             additionalInput = null;
         }
 
+        BigInteger s;
+
         if (additionalInput != null)
         {
             // Note: we ignore the use of pad8 on the additional input as we mandate byte arrays for it.
             additionalInput = Utils.hash_df(_digest, additionalInput, _seedlen);
+            s = new BigInteger(1, xor(_s, additionalInput));
         }
+        else
+        {
+            s = new BigInteger(1, _s);
+        }
+
+        // make sure we start with a clean output array.
+        Arrays.fill(output, (byte)0);
+
+        int outOffset = 0;
 
         for (int i = 0; i < m; i++)
         {
-            BigInteger t = new BigInteger(1, xor(_s, additionalInput));
-
-            _s = _P.multiply(t).getX().toBigInteger().toByteArray();
+            s = getScalarMultipleXCoord(_P, s);
 
             //System.err.println("S: " + new String(Hex.encode(_s)));
 
-            byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray();
+            byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray();
 
             if (r.length > _outlen)
             {
-                System.arraycopy(r, r.length - _outlen, output, i * _outlen, _outlen);
+                System.arraycopy(r, r.length - _outlen, output, outOffset, _outlen);
             }
             else
             {
-                System.arraycopy(r, 0, output, i * _outlen + (_outlen - r.length), r.length);
+                System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), r.length);
             }
 
             //System.err.println("R: " + new String(Hex.encode(r)));
-            additionalInput = null;
+            outOffset += _outlen;
 
             _reseedCounter++;
         }
 
-        if (m * _outlen < output.length)
+        if (outOffset < output.length)
         {
-            BigInteger t = new BigInteger(1, xor(_s, additionalInput));
+            s = getScalarMultipleXCoord(_P, s);
 
-            _s = _P.multiply(t).getX().toBigInteger().toByteArray();
+            byte[] r = _Q.multiply(s).normalize().getAffineXCoord().toBigInteger().toByteArray();
 
-            byte[] r = _Q.multiply(new BigInteger(1, _s)).getX().toBigInteger().toByteArray();
+            int required = output.length - outOffset;
 
-            System.arraycopy(r, 0, output, m * _outlen, output.length - (m * _outlen));
+            if (r.length > _outlen)
+            {
+                System.arraycopy(r, r.length - _outlen, output, outOffset, required);
+            }
+            else
+            {
+                System.arraycopy(r, 0, output, outOffset + (_outlen - r.length), required);
+            }
+
+            _reseedCounter++;
         }
 
         // Need to preserve length of S as unsigned int.
-        _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(new BigInteger(1, _s)).getX().toBigInteger());
+        _s = BigIntegers.asUnsignedByteArray(_sLength, _P.multiply(s).normalize().getAffineXCoord().toBigInteger());
 
         return numberOfBits;
     }
@@ -264,4 +299,9 @@
 
         return s;
     }
+
+    private BigInteger getScalarMultipleXCoord(ECPoint p, BigInteger s)
+    {
+        return p.multiply(s).normalize().getAffineXCoord().toBigInteger();
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html
deleted file mode 100644
index 630809b..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/drbg/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-NIST Deterministic Random Bit Generators (SP 800-90A).
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html
deleted file mode 100644
index 9ad3854..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/prng/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Lightweight psuedo-random number generators and SecureRandom variants and builders.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java
new file mode 100644
index 0000000..fced06e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSAKCalculator.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+/**
+ * Interface define calculators of K values for DSA/ECDSA.
+ */
+public interface DSAKCalculator
+{
+    /**
+     * Return true if this calculator is deterministic, false otherwise.
+     *
+     * @return true if deterministic, otherwise false.
+     */
+    boolean isDeterministic();
+
+    /**
+     * Non-deterministic initialiser.
+     *
+     * @param n the order of the DSA group.
+     * @param random a source of randomness.
+     */
+    void init(BigInteger n, SecureRandom random);
+
+    /**
+     * Deterministic initialiser.
+     *
+     * @param n the order of the DSA group.
+     * @param d the DSA private value.
+     * @param message the message being signed.
+     */
+    void init(BigInteger n, BigInteger d, byte[] message);
+
+    /**
+     * Return the next valid value of K.
+     *
+     * @return a K value.
+     */
+    BigInteger nextK();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
index a96cef0..292c408 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSASigner.java
@@ -1,5 +1,8 @@
 package org.bouncycastle.crypto.signers;
 
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DSA;
 import org.bouncycastle.crypto.params.DSAKeyParameters;
@@ -8,9 +11,6 @@
 import org.bouncycastle.crypto.params.DSAPublicKeyParameters;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 
-import java.math.BigInteger;
-import java.security.SecureRandom;
-
 /**
  * The Digital Signature Algorithm - as described in "Handbook of Applied
  * Cryptography", pages 452 - 453.
@@ -18,9 +18,28 @@
 public class DSASigner
     implements DSA
 {
-    DSAKeyParameters key;
+    private final DSAKCalculator kCalculator;
 
-    SecureRandom    random;
+    private DSAKeyParameters key;
+    private SecureRandom    random;
+
+    /**
+     * Default configuration, random K values.
+     */
+    public DSASigner()
+    {
+        this.kCalculator = new RandomDSAKCalculator();
+    }
+
+    /**
+     * Configuration with an alternate, possibly deterministic calculator of K.
+     *
+     * @param kCalculator a K value calculator.
+     */
+    public DSASigner(DSAKCalculator kCalculator)
+    {
+        this.kCalculator = kCalculator;
+    }
 
     public void init(
         boolean                 forSigning,
@@ -59,14 +78,17 @@
     {
         DSAParameters   params = key.getParameters();
         BigInteger      m = calculateE(params.getQ(), message);
-        BigInteger      k;
-        int                  qBitLength = params.getQ().bitLength();
 
-        do 
+        if (kCalculator.isDeterministic())
         {
-            k = new BigInteger(qBitLength, random);
+            kCalculator.init(params.getQ(), ((DSAPrivateKeyParameters)key).getX(), message);
         }
-        while (k.compareTo(params.getQ()) >= 0);
+        else
+        {
+            kCalculator.init(params.getQ(), random);
+        }
+
+        BigInteger  k = kCalculator.nextK();
 
         BigInteger  r = params.getG().modPow(k, params.getP()).mod(params.getQ());
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
index a8fc194..0e76950 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/DSTU4145Signer.java
@@ -5,6 +5,7 @@
 
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.DSA;
+import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECKeyParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
@@ -56,12 +57,17 @@
 
     public BigInteger[] generateSignature(byte[] message)
     {
-        ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message);
-        if (h.toBigInteger().signum() == 0)
+        ECDomainParameters parameters = key.getParameters();
+
+        ECCurve curve = parameters.getCurve();
+
+        ECFieldElement h = hash2FieldElement(curve, message);
+        if (h.isZero())
         {
-            h = key.getParameters().getCurve().fromBigInteger(ONE);
+            h = curve.fromBigInteger(ONE);
         }
 
+        BigInteger n = parameters.getN();
         BigInteger e, r, s;
         ECFieldElement Fe, y;
 
@@ -71,17 +77,17 @@
             {
                 do
                 {
-                    e = generateRandomInteger(key.getParameters().getN(), random);
-                    Fe = key.getParameters().getG().multiply(e).getX();
+                    e = generateRandomInteger(n, random);
+                    Fe = parameters.getG().multiply(e).normalize().getAffineXCoord();
                 }
-                while (Fe.toBigInteger().signum() == 0);
+                while (Fe.isZero());
 
                 y = h.multiply(Fe);
-                r = fieldElement2Integer(key.getParameters().getN(), y);
+                r = fieldElement2Integer(n, y);
             }
             while (r.signum() == 0);
 
-            s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(key.getParameters().getN());
+            s = r.multiply(((ECPrivateKeyParameters)key).getD()).add(e).mod(n);
         }
         while (s.signum() == 0);
 
@@ -90,22 +96,28 @@
 
     public boolean verifySignature(byte[] message, BigInteger r, BigInteger s)
     {
-        if (r.signum() == 0 || s.signum() == 0)
-        {
-            return false;
-        }
-        if (r.compareTo(key.getParameters().getN()) >= 0 || s.compareTo(key.getParameters().getN()) >= 0)
+        if (r.signum() <= 0 || s.signum() <= 0)
         {
             return false;
         }
 
-        ECFieldElement h = hash2FieldElement(key.getParameters().getCurve(), message);
-        if (h.toBigInteger().signum() == 0)
+        ECDomainParameters parameters = key.getParameters();
+
+        BigInteger n = parameters.getN();
+        if (r.compareTo(n) >= 0 || s.compareTo(n) >= 0)
         {
-            h = key.getParameters().getCurve().fromBigInteger(ONE);
+            return false;
         }
 
-        ECPoint R = ECAlgorithms.sumOfTwoMultiplies(key.getParameters().getG(), s, ((ECPublicKeyParameters)key).getQ(), r);
+        ECCurve curve = parameters.getCurve();
+
+        ECFieldElement h = hash2FieldElement(curve, message);
+        if (h.isZero())
+        {
+            h = curve.fromBigInteger(ONE);
+        }
+
+        ECPoint R = ECAlgorithms.sumOfTwoMultiplies(parameters.getG(), s, ((ECPublicKeyParameters)key).getQ(), r).normalize();
 
         // components must be bogus.
         if (R.isInfinity())
@@ -113,8 +125,8 @@
             return false;
         }
 
-        ECFieldElement y = h.multiply(R.getX());
-        return fieldElement2Integer(key.getParameters().getN(), y).compareTo(r) == 0;
+        ECFieldElement y = h.multiply(R.getAffineXCoord());
+        return fieldElement2Integer(n, y).compareTo(r) == 0;
     }
 
     /**
@@ -142,7 +154,7 @@
         byte[] data = Arrays.clone(hash);
         reverseBytes(data);
         BigInteger num = new BigInteger(1, data);
-        while (num.bitLength() >= curve.getFieldSize())
+        while (num.bitLength() > curve.getFieldSize())
         {
             num = num.clearBit(num.bitLength() - 1);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
index a80c574..2a1f98e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECDSASigner.java
@@ -19,9 +19,28 @@
 public class ECDSASigner
     implements ECConstants, DSA
 {
-    ECKeyParameters key;
+    private final DSAKCalculator kCalculator;
 
-    SecureRandom    random;
+    private ECKeyParameters key;
+    private SecureRandom    random;
+
+    /**
+     * Default configuration, random K values.
+     */
+    public ECDSASigner()
+    {
+        this.kCalculator = new RandomDSAKCalculator();
+    }
+
+    /**
+     * Configuration with an alternate, possibly deterministic calculator of K.
+     *
+     * @param kCalculator a K value calculator.
+     */
+    public ECDSASigner(DSAKCalculator kCalculator)
+    {
+        this.kCalculator = kCalculator;
+    }
 
     public void init(
         boolean                 forSigning,
@@ -64,24 +83,28 @@
         BigInteger r = null;
         BigInteger s = null;
 
+        if (kCalculator.isDeterministic())
+        {
+            kCalculator.init(n, ((ECPrivateKeyParameters)key).getD(), message);
+        }
+        else
+        {
+            kCalculator.init(n, random);
+        }
+
         // 5.3.2
         do // generate s
         {
             BigInteger k = null;
-            int        nBitLength = n.bitLength();
 
             do // generate r
             {
-                do
-                {
-                    k = new BigInteger(nBitLength, random);
-                }
-                while (k.equals(ZERO) || k.compareTo(n) >= 0);
+                k = kCalculator.nextK();
 
-                ECPoint p = key.getParameters().getG().multiply(k);
+                ECPoint p = key.getParameters().getG().multiply(k).normalize();
 
                 // 5.3.3
-                BigInteger x = p.getX().toBigInteger();
+                BigInteger x = p.getAffineXCoord().toBigInteger();
 
                 r = x.mod(n);
             }
@@ -135,7 +158,7 @@
         ECPoint G = key.getParameters().getG();
         ECPoint Q = ((ECPublicKeyParameters)key).getQ();
 
-        ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2);
+        ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, u1, Q, u2).normalize();
 
         // components must be bogus.
         if (point.isInfinity())
@@ -143,7 +166,7 @@
             return false;
         }
 
-        BigInteger v = point.getX().toBigInteger().mod(n);
+        BigInteger v = point.getAffineXCoord().toBigInteger().mod(n);
 
         return v.equals(r);
     }
@@ -153,17 +176,11 @@
         int log2n = n.bitLength();
         int messageBitLength = message.length * 8;
 
-        if (log2n >= messageBitLength)
+        BigInteger e = new BigInteger(1, message);
+        if (log2n < messageBitLength)
         {
-            return new BigInteger(1, message);
+            e = e.shiftRight(messageBitLength - log2n);
         }
-        else
-        {
-            BigInteger trunc = new BigInteger(1, message);
-
-            trunc = trunc.shiftRight(messageBitLength - log2n);
-
-            return trunc;
-        }
+        return e;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
index 7256d35..f6d7f4f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECGOST3410Signer.java
@@ -82,9 +82,9 @@
                 }
                 while (k.equals(ECConstants.ZERO));
 
-                ECPoint p = key.getParameters().getG().multiply(k);
+                ECPoint p = key.getParameters().getG().multiply(k).normalize();
 
-                BigInteger x = p.getX().toBigInteger();
+                BigInteger x = p.getAffineXCoord().toBigInteger();
 
                 r = x.mod(n);
             }
@@ -143,7 +143,7 @@
         ECPoint G = key.getParameters().getG(); // P
         ECPoint Q = ((ECPublicKeyParameters)key).getQ();
 
-        ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2);
+        ECPoint point = ECAlgorithms.sumOfTwoMultiplies(G, z1, Q, z2).normalize();
 
         // components must be bogus.
         if (point.isInfinity())
@@ -151,7 +151,7 @@
             return false;
         }
 
-        BigInteger R = point.getX().toBigInteger().mod(n);
+        BigInteger R = point.getAffineXCoord().toBigInteger().mod(n);
 
         return R.equals(r);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
index 07e8ca7..72bbbcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/ECNRSigner.java
@@ -101,8 +101,8 @@
 
             //    BigInteger Vx = tempPair.getPublic().getW().getAffineX();
             ECPublicKeyParameters V = (ECPublicKeyParameters)tempPair.getPublic();        // get temp's public key
-            BigInteger Vx = V.getQ().getX().toBigInteger();        // get the point's x coordinate
-            
+            BigInteger Vx = V.getQ().normalize().getAffineXCoord().toBigInteger();        // get the point's x coordinate
+
             r = Vx.add(e).mod(n);
         }
         while (r.equals(ECConstants.ZERO));
@@ -172,7 +172,7 @@
         ECPoint G = pubKey.getParameters().getG();
         ECPoint W = pubKey.getQ();
         // calculate P using Bouncy math
-        ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r);
+        ECPoint P = ECAlgorithms.sumOfTwoMultiplies(G, s, W, r).normalize();
 
         // components must be bogus.
         if (P.isInfinity())
@@ -180,7 +180,7 @@
             return false;
         }
 
-        BigInteger x = P.getX().toBigInteger();
+        BigInteger x = P.getAffineXCoord().toBigInteger();
         BigInteger t = r.subtract(x).mod(n);
 
         return t.equals(e);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
new file mode 100644
index 0000000..b96e3f3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/HMacDSAKCalculator.java
@@ -0,0 +1,161 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
+/**
+ * A deterministic K calculator based on the algorithm in section 3.2 of RFC 6979.
+ */
+public class HMacDSAKCalculator
+    implements DSAKCalculator
+{
+    private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+    private final HMac hMac;
+    private final byte[] K;
+    private final byte[] V;
+
+    private BigInteger n;
+
+    /**
+     * Base constructor.
+     *
+     * @param digest digest to build the HMAC on.
+     */
+    public HMacDSAKCalculator(Digest digest)
+    {
+        this.hMac = new HMac(digest);
+        this.V = new byte[hMac.getMacSize()];
+        this.K = new byte[hMac.getMacSize()];
+    }
+
+    public boolean isDeterministic()
+    {
+        return true;
+    }
+
+    public void init(BigInteger n, SecureRandom random)
+    {
+        throw new IllegalStateException("Operation not supported");
+    }
+
+    public void init(BigInteger n, BigInteger d, byte[] message)
+    {
+        this.n = n;
+
+        Arrays.fill(V, (byte)0x01);
+        Arrays.fill(K, (byte)0);
+
+        byte[] x = new byte[(n.bitLength() + 7) / 8];
+        byte[] dVal = BigIntegers.asUnsignedByteArray(d);
+
+        System.arraycopy(dVal, 0, x, x.length - dVal.length, dVal.length);
+
+        byte[] m = new byte[(n.bitLength() + 7) / 8];
+
+        BigInteger mInt = bitsToInt(message);
+
+        if (mInt.compareTo(n) > 0)
+        {
+            mInt = mInt.subtract(n);
+        }
+
+        byte[] mVal = BigIntegers.asUnsignedByteArray(mInt);
+
+        System.arraycopy(mVal, 0, m, m.length - mVal.length, mVal.length);
+
+        hMac.init(new KeyParameter(K));
+
+        hMac.update(V, 0, V.length);
+        hMac.update((byte)0x00);
+        hMac.update(x, 0, x.length);
+        hMac.update(m, 0, m.length);
+
+        hMac.doFinal(K, 0);
+
+        hMac.init(new KeyParameter(K));
+
+        hMac.update(V, 0, V.length);
+
+        hMac.doFinal(V, 0);
+
+        hMac.update(V, 0, V.length);
+        hMac.update((byte)0x01);
+        hMac.update(x, 0, x.length);
+        hMac.update(m, 0, m.length);
+
+        hMac.doFinal(K, 0);
+
+        hMac.init(new KeyParameter(K));
+
+        hMac.update(V, 0, V.length);
+
+        hMac.doFinal(V, 0);
+    }
+
+    public BigInteger nextK()
+    {
+        byte[] t = new byte[((n.bitLength() + 7) / 8)];
+
+        for (;;)
+        {
+            int tOff = 0;
+
+            while (tOff < t.length)
+            {
+                hMac.update(V, 0, V.length);
+
+                hMac.doFinal(V, 0);
+
+                if (t.length - tOff < V.length)
+                {
+                    System.arraycopy(V, 0, t, tOff, t.length - tOff);
+                    tOff += t.length - tOff;
+                }
+                else
+                {
+                    System.arraycopy(V, 0, t, tOff, V.length);
+                    tOff += V.length;
+                }
+            }
+
+            BigInteger k = bitsToInt(t);
+
+            if (k.equals(ZERO) || k.compareTo(n) >= 0)
+            {
+                hMac.update(V, 0, V.length);
+                hMac.update((byte)0x00);
+
+                hMac.doFinal(K, 0);
+
+                hMac.init(new KeyParameter(K));
+
+                hMac.update(V, 0, V.length);
+
+                hMac.doFinal(V, 0);
+            }
+            else
+            {
+                return k;
+            }
+        }
+    }
+
+    private BigInteger bitsToInt(byte[] t)
+    {
+        BigInteger v = new BigInteger(1, t);
+
+        if (t.length * 8 > n.bitLength())
+        {
+            v = v.shiftRight(t.length * 8 - n.bitLength());
+        }
+
+        return v;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
index f33ed31..aaae064 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RSADigestSigner.java
@@ -57,9 +57,15 @@
     public RSADigestSigner(
         Digest digest)
     {
-        this.digest = digest;
+        this(digest, (ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName()));
+    }
 
-        algId = new AlgorithmIdentifier((ASN1ObjectIdentifier)oidMap.get(digest.getAlgorithmName()), DERNull.INSTANCE);
+    public RSADigestSigner(
+        Digest digest,
+        ASN1ObjectIdentifier digestOid)
+    {
+        this.digest = digest;
+        this.algId = new AlgorithmIdentifier(digestOid, DERNull.INSTANCE);
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
new file mode 100644
index 0000000..bbd8cda
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/signers/RandomDSAKCalculator.java
@@ -0,0 +1,43 @@
+package org.bouncycastle.crypto.signers;
+
+import java.math.BigInteger;
+import java.security.SecureRandom;
+
+class RandomDSAKCalculator
+    implements DSAKCalculator
+{
+    private static final BigInteger ZERO = BigInteger.valueOf(0);
+
+    private BigInteger q;
+    private SecureRandom random;
+
+    public boolean isDeterministic()
+    {
+        return false;
+    }
+
+    public void init(BigInteger n, SecureRandom random)
+    {
+        this.q = n;
+        this.random = random;
+    }
+
+    public void init(BigInteger n, BigInteger d, byte[] message)
+    {
+        throw new IllegalStateException("Operation not supported");
+    }
+
+    public BigInteger nextK()
+    {
+        int qBitLength = q.bitLength();
+
+        BigInteger k;
+        do
+        {
+            k = new BigInteger(qBitLength, random);
+        }
+        while (k.equals(ZERO) || k.compareTo(q) >= 0);
+
+        return k;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html
deleted file mode 100644
index 151d3d5..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/signers/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Basic signers.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java
new file mode 100644
index 0000000..ef7f4fb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsAgreementCredentials.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsAgreementCredentials
+    extends AbstractTlsCredentials
+    implements TlsAgreementCredentials
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
index 9c2a526..71a2cab 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCipherFactory.java
@@ -5,11 +5,9 @@
 public class AbstractTlsCipherFactory
     implements TlsCipherFactory
 {
-
     public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
         throws IOException
     {
-
         throw new TlsFatalAlert(AlertDescription.internal_error);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
index 9e113f9..7d4fd03 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsClient.java
@@ -8,12 +8,13 @@
     extends AbstractTlsPeer
     implements TlsClient
 {
-
     protected TlsCipherFactory cipherFactory;
 
     protected TlsClientContext context;
 
     protected Vector supportedSignatureAlgorithms;
+    protected int[] namedCurves;
+    protected short[] clientECPointFormats, serverECPointFormats;
 
     protected int selectedCipherSuite;
     protected short selectedCompressionMethod;
@@ -33,6 +34,11 @@
         this.context = context;
     }
 
+    public TlsSession getSessionToResume()
+    {
+        return null;
+    }
+
     /**
      * RFC 5246 E.1. "TLS clients that wish to negotiate with older servers MAY send any value
      * {03,XX} as the record layer version number. Typical values would be {03,00}, the lowest
@@ -46,7 +52,7 @@
         // return ProtocolVersion.SSLv3;
 
         // "the lowest version number supported by the client"
-        // return getMinimumServerVersion();
+        // return getMinimumVersion();
 
         // "the value of ClientHello.client_version"
         return getClientVersion();
@@ -54,13 +60,12 @@
 
     public ProtocolVersion getClientVersion()
     {
-        return ProtocolVersion.TLSv11;
+        return ProtocolVersion.TLSv12;
     }
 
     public Hashtable getClientExtensions()
         throws IOException
     {
-
         Hashtable clientExtensions = null;
 
         ProtocolVersion clientVersion = context.getClientVersion();
@@ -71,14 +76,13 @@
          */
         if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
         {
-
             // TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
 
-            short[] hashAlgorithms = new short[]{HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
-                HashAlgorithm.sha224, HashAlgorithm.sha1};
+            short[] hashAlgorithms = new short[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
+                HashAlgorithm.sha224, HashAlgorithm.sha1 };
 
             // TODO Sort out ECDSA signatures and add them as the preferred option here
-            short[] signatureAlgorithms = new short[]{SignatureAlgorithm.rsa};
+            short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa };
 
             this.supportedSignatureAlgorithms = new Vector();
             for (int i = 0; i < hashAlgorithms.length; ++i)
@@ -96,14 +100,33 @@
             this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
                 SignatureAlgorithm.dsa));
 
-            if (clientExtensions == null)
-            {
-                clientExtensions = new Hashtable();
-            }
+            clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);
 
             TlsUtils.addSignatureAlgorithmsExtension(clientExtensions, supportedSignatureAlgorithms);
         }
 
+        if (TlsECCUtils.containsECCCipherSuites(getCipherSuites()))
+        {
+            /*
+             * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
+             * appends these extensions (along with any others), enumerating the curves it supports
+             * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
+             * Curves Extension and the Supported Point Formats Extension.
+             */
+            /*
+             * TODO Could just add all the curves since we support them all, but users may not want
+             * to use unnecessarily large fields. Need configuration options.
+             */
+            this.namedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 };
+            this.clientECPointFormats = new short[]{ ECPointFormat.uncompressed,
+                ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
+
+            clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);
+
+            TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves);
+            TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats);
+        }
+
         return clientExtensions;
     }
 
@@ -141,19 +164,6 @@
         this.selectedCompressionMethod = selectedCompressionMethod;
     }
 
-    public void notifySecureRenegotiation(boolean secureRenegotiation)
-        throws IOException
-    {
-        if (!secureRenegotiation)
-        {
-            /*
-             * RFC 5746 3.4. In this case, some clients may want to terminate the handshake instead
-             * of continuing; see Section 4.1 for discussion.
-             */
-            // throw new TlsFatalAlert(AlertDescription.handshake_failure);
-        }
-    }
-
     public void processServerExtensions(Hashtable serverExtensions)
         throws IOException
     {
@@ -170,6 +180,18 @@
             {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
             }
+
+            int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions);
+            if (namedCurves != null)
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+
+            this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
+            if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
         }
     }
 
@@ -210,9 +232,4 @@
         throws IOException
     {
     }
-
-    public void notifyHandshakeComplete()
-        throws IOException
-    {
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
index 1ff67e3..5e02892 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsContext.java
@@ -5,12 +5,12 @@
 abstract class AbstractTlsContext
     implements TlsContext
 {
-
     private SecureRandom secureRandom;
     private SecurityParameters securityParameters;
 
     private ProtocolVersion clientVersion = null;
     private ProtocolVersion serverVersion = null;
+    private TlsSession session = null;
     private Object userObject = null;
 
     AbstractTlsContext(SecureRandom secureRandom, SecurityParameters securityParameters)
@@ -34,7 +34,7 @@
         return clientVersion;
     }
 
-    public void setClientVersion(ProtocolVersion clientVersion)
+    void setClientVersion(ProtocolVersion clientVersion)
     {
         this.clientVersion = clientVersion;
     }
@@ -44,11 +44,21 @@
         return serverVersion;
     }
 
-    public void setServerVersion(ProtocolVersion serverVersion)
+    void setServerVersion(ProtocolVersion serverVersion)
     {
         this.serverVersion = serverVersion;
     }
 
+    public TlsSession getResumableSession()
+    {
+        return session;
+    }
+
+    void setResumableSession(TlsSession session)
+    {
+        this.session = session;
+    }
+
     public Object getUserObject()
     {
         return userObject;
@@ -61,6 +71,10 @@
 
     public byte[] exportKeyingMaterial(String asciiLabel, byte[] context_value, int length)
     {
+        if (context_value != null && !TlsUtils.isValidUint16(context_value.length))
+        {
+            throw new IllegalArgumentException("'context_value' must have length less than 2^16 (or be null)");
+        }
 
         SecurityParameters sp = getSecurityParameters();
         byte[] cr = sp.getClientRandom(), sr = sp.getServerRandom();
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java
new file mode 100644
index 0000000..b98743f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsCredentials.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsCredentials
+    implements TlsCredentials
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java
new file mode 100644
index 0000000..e6ff39b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsEncryptionCredentials.java
@@ -0,0 +1,7 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsEncryptionCredentials
+    extends AbstractTlsCredentials
+    implements TlsEncryptionCredentials
+{
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
index 85057c1..43e80e6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsKeyExchange.java
@@ -7,7 +7,6 @@
 public abstract class AbstractTlsKeyExchange
     implements TlsKeyExchange
 {
-
     protected int keyExchange;
     protected Vector supportedSignatureAlgorithms;
 
@@ -27,7 +26,6 @@
 
         if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
         {
-
             /*
              * RFC 5264 7.4.1.4.1. If the client does not send the signature_algorithms extension,
              * the server MUST do the following:
@@ -45,7 +43,6 @@
             {
                 switch (keyExchange)
                 {
-
                 case KeyExchangeAlgorithm.DH_DSS:
                 case KeyExchangeAlgorithm.DHE_DSS:
                 case KeyExchangeAlgorithm.SRP_DSS:
@@ -73,6 +70,12 @@
                     break;
                 }
 
+                case KeyExchangeAlgorithm.DHE_PSK:
+                case KeyExchangeAlgorithm.ECDHE_PSK:
+                case KeyExchangeAlgorithm.PSK:
+                case KeyExchangeAlgorithm.SRP:
+                    break;
+
                 default:
                     throw new IllegalStateException("unsupported key exchange algorithm");
                 }
@@ -88,7 +91,6 @@
     public void processServerCertificate(Certificate serverCertificate)
         throws IOException
     {
-
         if (supportedSignatureAlgorithms == null)
         {
             /*
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
index bdfd0d5..80d6af7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsPeer.java
@@ -1,8 +1,21 @@
 package org.bouncycastle.crypto.tls;
 
+import java.io.IOException;
+
 public abstract class AbstractTlsPeer
     implements TlsPeer
 {
+    public void notifySecureRenegotiation(boolean secureRenegotiation) throws IOException
+    {
+        if (!secureRenegotiation)
+        {
+            /*
+             * RFC 5746 3.4/3.6. In this case, some clients/servers may want to terminate the handshake instead
+             * of continuing; see Section 4.1/4.3 for discussion.
+             */
+            throw new TlsFatalAlert(AlertDescription.handshake_failure);
+        }
+    }
 
     public void notifyAlertRaised(short alertLevel, short alertDescription, String message, Exception cause)
     {
@@ -11,4 +24,8 @@
     public void notifyAlertReceived(short alertLevel, short alertDescription)
     {
     }
+
+    public void notifyHandshakeComplete() throws IOException
+    {
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
index 8235fd1..bd428a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsServer.java
@@ -4,11 +4,12 @@
 import java.util.Hashtable;
 import java.util.Vector;
 
+import org.bouncycastle.util.Arrays;
+
 public abstract class AbstractTlsServer
     extends AbstractTlsPeer
     implements TlsServer
 {
-
     protected TlsCipherFactory cipherFactory;
 
     protected TlsServerContext context;
@@ -18,6 +19,8 @@
     protected short[] offeredCompressionMethods;
     protected Hashtable clientExtensions;
 
+    protected short maxFragmentLengthOffered;
+    protected boolean truncatedHMacOffered;
     protected Vector supportedSignatureAlgorithms;
     protected boolean eccCipherSuitesOffered;
     protected int[] namedCurves;
@@ -38,6 +41,16 @@
         this.cipherFactory = cipherFactory;
     }
 
+    protected boolean allowTruncatedHMac()
+    {
+        return false;
+    }
+
+    protected Hashtable checkServerExtensions()
+    {
+        return this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.serverExtensions);
+    }
+
     protected abstract int[] getCipherSuites();
 
     protected short[] getCompressionMethods()
@@ -57,7 +70,6 @@
 
     protected boolean supportsClientECCCapabilities(int[] namedCurves, short[] ecPointFormats)
     {
-
         // NOTE: BC supports all the current set of point formats so we don't check them here
 
         if (namedCurves == null)
@@ -106,27 +118,15 @@
         this.offeredCompressionMethods = offeredCompressionMethods;
     }
 
-    public void notifySecureRenegotiation(boolean secureRenegotiation)
-        throws IOException
-    {
-        if (!secureRenegotiation)
-        {
-            /*
-             * RFC 5746 3.6. In this case, some servers may want to terminate the handshake instead
-             * of continuing; see Section 4.3 for discussion.
-             */
-            throw new TlsFatalAlert(AlertDescription.handshake_failure);
-        }
-    }
-
     public void processClientExtensions(Hashtable clientExtensions)
         throws IOException
     {
-
         this.clientExtensions = clientExtensions;
 
         if (clientExtensions != null)
         {
+            this.maxFragmentLengthOffered = TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions);
+            this.truncatedHMacOffered = TlsExtensionsUtils.hasTruncatedHMacExtension(clientExtensions);
 
             this.supportedSignatureAlgorithms = TlsUtils.getSignatureAlgorithmsExtension(clientExtensions);
             if (this.supportedSignatureAlgorithms != null)
@@ -176,7 +176,6 @@
     public int getSelectedCipherSuite()
         throws IOException
     {
-
         /*
          * TODO RFC 5246 7.4.3. In order to negotiate correctly, the server MUST check any candidate
          * cipher suites against the "signature_algorithms" extension before selecting them. This is
@@ -197,7 +196,9 @@
         for (int i = 0; i < cipherSuites.length; ++i)
         {
             int cipherSuite = cipherSuites[i];
-            if (TlsProtocol.arrayContains(this.offeredCipherSuites, cipherSuite)
+
+            // TODO Certain cipher suites may only be available starting at a particular version
+            if (Arrays.contains(this.offeredCipherSuites, cipherSuite)
                 && (eccCipherSuitesEnabled || !TlsECCUtils.isECCCipherSuite(cipherSuite)))
             {
                 return this.selectedCipherSuite = cipherSuite;
@@ -212,7 +213,7 @@
         short[] compressionMethods = getCompressionMethods();
         for (int i = 0; i < compressionMethods.length; ++i)
         {
-            if (TlsProtocol.arrayContains(offeredCompressionMethods, compressionMethods[i]))
+            if (Arrays.contains(offeredCompressionMethods, compressionMethods[i]))
             {
                 return this.selectedCompressionMethod = compressionMethods[i];
             }
@@ -224,6 +225,15 @@
     public Hashtable getServerExtensions()
         throws IOException
     {
+        if (this.maxFragmentLengthOffered >= 0)
+        {
+            TlsExtensionsUtils.addMaxFragmentLengthExtension(checkServerExtensions(), this.maxFragmentLengthOffered);
+        }
+
+        if (this.truncatedHMacOffered && allowTruncatedHMac())
+        {
+            TlsExtensionsUtils.addTruncatedHMacExtension(checkServerExtensions());
+        }
 
         if (this.clientECPointFormats != null && TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
         {
@@ -232,15 +242,13 @@
              * message including a Supported Point Formats Extension appends this extension (along
              * with others) to its ServerHello message, enumerating the point formats it can parse.
              */
-            this.serverECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2,
-                ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed};
+            this.serverECPointFormats = new short[]{ ECPointFormat.ansiX962_compressed_char2,
+                ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed };
 
-            this.serverExtensions = new Hashtable();
-            TlsECCUtils.addSupportedPointFormatsExtension(serverExtensions, serverECPointFormats);
-            return serverExtensions;
+            TlsECCUtils.addSupportedPointFormatsExtension(checkServerExtensions(), serverECPointFormats);
         }
 
-        return null;
+        return serverExtensions;
     }
 
     public Vector getServerSupplementalData()
@@ -249,7 +257,14 @@
         return null;
     }
 
+    public CertificateStatus getCertificateStatus()
+        throws IOException
+    {
+        return null;
+    }
+
     public CertificateRequest getCertificateRequest()
+        throws IOException
     {
         return null;
     }
@@ -296,9 +311,4 @@
          */
         return new NewSessionTicket(0L, TlsUtils.EMPTY_BYTES);
     }
-
-    public void notifyHandshakeComplete()
-        throws IOException
-    {
-    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
index a0c24c7..3a1d631 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSigner.java
@@ -1,13 +1,38 @@
 package org.bouncycastle.crypto.tls;
 
+import org.bouncycastle.crypto.CryptoException;
+import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
 public abstract class AbstractTlsSigner
     implements TlsSigner
 {
-
     protected TlsContext context;
 
     public void init(TlsContext context)
     {
         this.context = context;
     }
+
+    public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+        throws CryptoException
+    {
+        return generateRawSignature(null, privateKey, md5AndSha1);
+    }
+
+    public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+        throws CryptoException
+    {
+        return verifyRawSignature(null, sigBytes, publicKey, md5AndSha1);
+    }
+
+    public Signer createSigner(AsymmetricKeyParameter privateKey)
+    {
+        return createSigner(null, privateKey);
+    }
+
+    public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+    {
+        return createVerifyer(null, publicKey);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java
new file mode 100644
index 0000000..3452f52
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AbstractTlsSignerCredentials.java
@@ -0,0 +1,11 @@
+package org.bouncycastle.crypto.tls;
+
+public abstract class AbstractTlsSignerCredentials
+    extends AbstractTlsCredentials
+    implements TlsSignerCredentials
+{
+    public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+    {
+        throw new IllegalStateException("TlsSignerCredentials implementation does not support (D)TLS 1.2+");
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
index 5e3269b..91366be 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/AlertDescription.java
@@ -5,11 +5,12 @@
  */
 public class AlertDescription
 {
-
     /**
      * This message notifies the recipient that the sender will not send any more messages on this
-     * connection. The session becomes unresumable if any connection is terminated without proper
-     * close_notify messages with level equal to warning.
+     * connection. Note that as of TLS 1.1, failure to properly close a connection no longer
+     * requires that a session not be resumed. This is a change from TLS 1.0 ("The session becomes
+     * unresumable if any connection is terminated without proper close_notify messages with level
+     * equal to warning.") to conform with widespread implementation practice.
      */
     public static final short close_notify = 0;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
index 8b9d4ab..7642c4a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ByteQueue.java
@@ -25,12 +25,12 @@
     /**
      * The initial size for our buffer.
      */
-    private static final int INITBUFSIZE = 1024;
+    private static final int DEFAULT_CAPACITY = 1024;
 
     /**
      * The buffer where we store our data.
      */
-    private byte[] databuf = new byte[ByteQueue.INITBUFSIZE];
+    private byte[] databuf;;
 
     /**
      * How many bytes at the beginning of the buffer are skipped.
@@ -42,6 +42,16 @@
      */
     private int available = 0;
 
+    public ByteQueue()
+    {
+        this(DEFAULT_CAPACITY);
+    }
+
+    public ByteQueue(int capacity)
+    {
+        databuf = new byte[capacity];
+    }
+
     /**
      * Read data from the buffer.
      *
@@ -62,26 +72,34 @@
                 + " is too small for a read of " + len + " bytes");
         }
         System.arraycopy(databuf, skipped + skip, buf, offset, len);
-        return;
     }
 
     /**
      * Add some data to our buffer.
      *
-     * @param data   A byte-array to read data from.
-     * @param offset How many bytes to skip at the beginning of the array.
-     * @param len    How many bytes to read from the array.
+     * @param buf A byte-array to read data from.
+     * @param off How many bytes to skip at the beginning of the array.
+     * @param len How many bytes to read from the array.
      */
-    public void addData(byte[] data, int offset, int len)
+    public void addData(byte[] buf, int off, int len)
     {
         if ((skipped + available + len) > databuf.length)
         {
-            byte[] tmp = new byte[ByteQueue.nextTwoPow(data.length)];
-            System.arraycopy(databuf, skipped, tmp, 0, available);
+            int desiredSize = ByteQueue.nextTwoPow(available + len);
+            if (desiredSize > databuf.length)
+            {
+                byte[] tmp = new byte[desiredSize];
+                System.arraycopy(databuf, skipped, tmp, 0, available);
+                databuf = tmp;
+            }
+            else
+            {
+                System.arraycopy(databuf, skipped, databuf, 0, available);
+            }
             skipped = 0;
-            databuf = tmp;
         }
-        System.arraycopy(data, offset, databuf, skipped + available, len);
+
+        System.arraycopy(buf, off, databuf, skipped + available, len);
         available += len;
     }
 
@@ -102,15 +120,27 @@
          */
         available -= i;
         skipped += i;
+    }
 
-        /*
-         * If more than half of our data is skipped, we will move the data in the buffer.
-         */
-        if (skipped > (databuf.length / 2))
-        {
-            System.arraycopy(databuf, skipped, databuf, 0, available);
-            skipped = 0;
-        }
+    /**
+     * Remove data from the buffer.
+     *
+     * @param buf The buffer where the removed data will be copied to.
+     * @param off How many bytes to skip at the beginning of buf.
+     * @param len How many bytes to read at all.
+     * @param skip How many bytes from our data to skip.
+     */
+    public void removeData(byte[] buf, int off, int len, int skip)
+    {
+        read(buf, off, len, skip);
+        removeData(skip + len);
+    }
+
+    public byte[] removeData(int len, int skip)
+    {
+        byte[] buf = new byte[len];
+        removeData(buf, 0, len, skip);
+        return buf;
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java
new file mode 100644
index 0000000..8902ed7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertChainType.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * RFC 3546 3.3.
+ */
+public class CertChainType
+{
+    public static final short individual_certs = 0;
+    public static final short pkipath = 1;
+
+    public static boolean isValid(short certChainType)
+    {
+        return certChainType >= individual_certs && certChainType <= pkipath;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
index fab79f4..02cf693 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/Certificate.java
@@ -7,7 +7,6 @@
 import java.util.Vector;
 
 import org.bouncycastle.asn1.ASN1Encoding;
-import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1Primitive;
 
 /**
@@ -25,7 +24,6 @@
  */
 public class Certificate
 {
-
     public static final Certificate EMPTY_CHAIN = new Certificate(
         new org.bouncycastle.asn1.x509.Certificate[0]);
 
@@ -46,7 +44,7 @@
      */
     public org.bouncycastle.asn1.x509.Certificate[] getCerts()
     {
-        return clone(certificateList);
+        return getCertificateList();
     }
 
     /**
@@ -55,7 +53,7 @@
      */
     public org.bouncycastle.asn1.x509.Certificate[] getCertificateList()
     {
-        return clone(certificateList);
+        return cloneCertificateList();
     }
 
     public org.bouncycastle.asn1.x509.Certificate getCertificateAt(int index)
@@ -86,21 +84,23 @@
     public void encode(OutputStream output)
         throws IOException
     {
-        Vector encCerts = new Vector(this.certificateList.length);
+        Vector derEncodings = new Vector(this.certificateList.length);
+
         int totalLength = 0;
         for (int i = 0; i < this.certificateList.length; ++i)
         {
-            byte[] encCert = certificateList[i].getEncoded(ASN1Encoding.DER);
-            encCerts.addElement(encCert);
-            totalLength += encCert.length + 3;
+            byte[] derEncoding = certificateList[i].getEncoded(ASN1Encoding.DER);
+            derEncodings.addElement(derEncoding);
+            totalLength += derEncoding.length + 3;
         }
 
+        TlsUtils.checkUint24(totalLength);
         TlsUtils.writeUint24(totalLength, output);
 
-        for (int i = 0; i < encCerts.size(); ++i)
+        for (int i = 0; i < derEncodings.size(); ++i)
         {
-            byte[] encCert = (byte[])encCerts.elementAt(i);
-            TlsUtils.writeOpaque24(encCert, output);
+            byte[] derEncoding = (byte[])derEncodings.elementAt(i);
+            TlsUtils.writeOpaque24(derEncoding, output);
         }
     }
 
@@ -114,40 +114,36 @@
     public static Certificate parse(InputStream input)
         throws IOException
     {
-        org.bouncycastle.asn1.x509.Certificate[] certs;
-        int left = TlsUtils.readUint24(input);
-        if (left == 0)
+        int totalLength = TlsUtils.readUint24(input);
+        if (totalLength == 0)
         {
             return EMPTY_CHAIN;
         }
-        Vector tmp = new Vector();
-        while (left > 0)
+
+        byte[] certListData = TlsUtils.readFully(totalLength, input);
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(certListData);
+
+        Vector certificate_list = new Vector();
+        while (buf.available() > 0)
         {
-            int size = TlsUtils.readUint24(input);
-            left -= 3 + size;
-
-            byte[] buf = TlsUtils.readFully(size, input);
-
-            ByteArrayInputStream bis = new ByteArrayInputStream(buf);
-            ASN1Primitive asn1 = new ASN1InputStream(bis).readObject();
-            TlsProtocol.assertEmpty(bis);
-
-            tmp.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1));
+            byte[] derEncoding = TlsUtils.readOpaque24(buf);
+            ASN1Primitive asn1Cert = TlsUtils.readDERObject(derEncoding);
+            certificate_list.addElement(org.bouncycastle.asn1.x509.Certificate.getInstance(asn1Cert));
         }
-        certs = new org.bouncycastle.asn1.x509.Certificate[tmp.size()];
-        for (int i = 0; i < tmp.size(); i++)
+
+        org.bouncycastle.asn1.x509.Certificate[] certificateList = new org.bouncycastle.asn1.x509.Certificate[certificate_list.size()];
+        for (int i = 0; i < certificate_list.size(); i++)
         {
-            certs[i] = (org.bouncycastle.asn1.x509.Certificate)tmp.elementAt(i);
+            certificateList[i] = (org.bouncycastle.asn1.x509.Certificate)certificate_list.elementAt(i);
         }
-        return new Certificate(certs);
+        return new Certificate(certificateList);
     }
 
-    private org.bouncycastle.asn1.x509.Certificate[] clone(org.bouncycastle.asn1.x509.Certificate[] list)
+    protected org.bouncycastle.asn1.x509.Certificate[] cloneCertificateList()
     {
-        org.bouncycastle.asn1.x509.Certificate[] rv = new org.bouncycastle.asn1.x509.Certificate[list.length];
-
-        System.arraycopy(list, 0, rv, 0, rv.length);
-
-        return rv;
+        org.bouncycastle.asn1.x509.Certificate[] result = new org.bouncycastle.asn1.x509.Certificate[certificateList.length];
+        System.arraycopy(certificateList, 0, result, 0, result.length);
+        return result;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
index 00bf950..e9606e3 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateRequest.java
@@ -25,8 +25,9 @@
  */
 public class CertificateRequest
 {
-    private short[] certificateTypes;
-    private Vector certificateAuthorities;
+    protected short[] certificateTypes;
+    protected Vector supportedSignatureAlgorithms;
+    protected Vector certificateAuthorities;
 
     /*
      * TODO RFC 5264 7.4.4 A list of the hash/signature algorithm pairs that the server is able to
@@ -37,9 +38,10 @@
      * @param certificateTypes       see {@link ClientCertificateType} for valid constants.
      * @param certificateAuthorities a {@link Vector} of {@link X500Name}.
      */
-    public CertificateRequest(short[] certificateTypes, Vector certificateAuthorities)
+    public CertificateRequest(short[] certificateTypes, Vector supportedSignatureAlgorithms, Vector certificateAuthorities)
     {
         this.certificateTypes = certificateTypes;
+        this.supportedSignatureAlgorithms = supportedSignatureAlgorithms;
         this.certificateAuthorities = certificateAuthorities;
     }
 
@@ -53,6 +55,14 @@
     }
 
     /**
+     * @return a {@link Vector} of {@link SignatureAndHashAlgorithm} (or null before TLS 1.2).
+     */
+    public Vector getSupportedSignatureAlgorithms()
+    {
+        return supportedSignatureAlgorithms;
+    }
+
+    /**
      * @return a {@link Vector} of {@link X500Name}
      */
     public Vector getCertificateAuthorities()
@@ -69,15 +79,19 @@
     public void encode(OutputStream output)
         throws IOException
     {
-
         if (certificateTypes == null || certificateTypes.length == 0)
         {
-            TlsUtils.writeUint8((short)0, output);
+            TlsUtils.writeUint8(0, output);
         }
         else
         {
-            TlsUtils.writeUint8((short)certificateTypes.length, output);
-            TlsUtils.writeUint8Array(certificateTypes, output);
+            TlsUtils.writeUint8ArrayWithUint8Length(certificateTypes, output);
+        }
+
+        if (supportedSignatureAlgorithms != null)
+        {
+            // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+            TlsUtils.encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, output);
         }
 
         if (certificateAuthorities == null || certificateAuthorities.isEmpty())
@@ -86,22 +100,23 @@
         }
         else
         {
+            Vector derEncodings = new Vector(certificateAuthorities.size());
 
-            Vector encDNs = new Vector(certificateAuthorities.size());
             int totalLength = 0;
             for (int i = 0; i < certificateAuthorities.size(); ++i)
             {
-                X500Name authorityDN = (X500Name)certificateAuthorities.elementAt(i);
-                byte[] encDN = authorityDN.getEncoded(ASN1Encoding.DER);
-                encDNs.addElement(encDN);
-                totalLength += encDN.length;
+                X500Name certificateAuthority = (X500Name)certificateAuthorities.elementAt(i);
+                byte[] derEncoding = certificateAuthority.getEncoded(ASN1Encoding.DER);
+                derEncodings.addElement(derEncoding);
+                totalLength += derEncoding.length;
             }
 
+            TlsUtils.checkUint16(totalLength);
             TlsUtils.writeUint16(totalLength, output);
 
-            for (int i = 0; i < encDNs.size(); ++i)
+            for (int i = 0; i < derEncodings.size(); ++i)
             {
-                byte[] encDN = (byte[])encDNs.elementAt(i);
+                byte[] encDN = (byte[])derEncodings.elementAt(i);
                 output.write(encDN);
             }
         }
@@ -109,12 +124,15 @@
 
     /**
      * Parse a {@link CertificateRequest} from an {@link InputStream}.
-     *
-     * @param input the {@link InputStream} to parse from.
+     * 
+     * @param context
+     *            the {@link TlsContext} of the current connection.
+     * @param input
+     *            the {@link InputStream} to parse from.
      * @return a {@link CertificateRequest} object.
      * @throws IOException
      */
-    public static CertificateRequest parse(InputStream input)
+    public static CertificateRequest parse(TlsContext context, InputStream input)
         throws IOException
     {
         int numTypes = TlsUtils.readUint8(input);
@@ -124,17 +142,23 @@
             certificateTypes[i] = TlsUtils.readUint8(input);
         }
 
-        byte[] authorities = TlsUtils.readOpaque16(input);
-
-        Vector authorityDNs = new Vector();
-
-        ByteArrayInputStream bis = new ByteArrayInputStream(authorities);
-        while (bis.available() > 0)
+        Vector supportedSignatureAlgorithms = null;
+        if (TlsUtils.isTLSv12(context))
         {
-            byte[] dnBytes = TlsUtils.readOpaque16(bis);
-            authorityDNs.addElement(X500Name.getInstance(ASN1Primitive.fromByteArray(dnBytes)));
+            // TODO Check whether SignatureAlgorithm.anonymous is allowed here
+            supportedSignatureAlgorithms = TlsUtils.parseSupportedSignatureAlgorithms(false, input);
         }
 
-        return new CertificateRequest(certificateTypes, authorityDNs);
+        Vector certificateAuthorities = new Vector();
+        byte[] certAuthData = TlsUtils.readOpaque16(input);
+        ByteArrayInputStream bis = new ByteArrayInputStream(certAuthData);
+        while (bis.available() > 0)
+        {
+            byte[] derEncoding = TlsUtils.readOpaque16(bis);
+            ASN1Primitive asn1 = TlsUtils.readDERObject(derEncoding);
+            certificateAuthorities.addElement(X500Name.getInstance(asn1));
+        }
+
+        return new CertificateRequest(certificateTypes, supportedSignatureAlgorithms, certificateAuthorities);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
new file mode 100644
index 0000000..34a0284
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatus.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ocsp.OCSPResponse;
+
+public class CertificateStatus
+{
+    protected short statusType;
+    protected Object response;
+
+    public CertificateStatus(short statusType, Object response)
+    {
+        if (!isCorrectType(statusType, response))
+        {
+            throw new IllegalArgumentException("'response' is not an instance of the correct type");
+        }
+        
+        this.statusType = statusType;
+        this.response = response;
+    }
+
+    public short getStatusType()
+    {
+        return statusType;
+    }
+
+    public Object getResponse()
+    {
+        return response;
+    }
+
+    public OCSPResponse getOCSPResponse()
+    {
+        if (!isCorrectType(CertificateStatusType.ocsp, response))
+        {
+            throw new IllegalStateException("'response' is not an OCSPResponse");
+        }
+        return (OCSPResponse)response;
+    }
+
+    /**
+     * Encode this {@link CertificateStatus} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        TlsUtils.writeUint8(statusType, output);
+
+        switch (statusType)
+        {
+        case CertificateStatusType.ocsp:
+            byte[] derEncoding = ((OCSPResponse) response).getEncoded(ASN1Encoding.DER);
+            TlsUtils.writeOpaque24(derEncoding, output);
+            break;
+        default:
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    /**
+     * Parse a {@link CertificateStatus} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link CertificateStatus} object.
+     * @throws IOException
+     */
+    public static CertificateStatus parse(InputStream input) throws IOException
+    {
+        short status_type = TlsUtils.readUint8(input);
+        Object response;
+
+        switch (status_type)
+        {
+        case CertificateStatusType.ocsp:
+        {
+            byte[] derEncoding = TlsUtils.readOpaque24(input);
+            response = OCSPResponse.getInstance(TlsUtils.readDERObject(derEncoding));
+            break;
+        }
+        default:
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        return new CertificateStatus(status_type, response);
+    }
+
+    protected static boolean isCorrectType(short statusType, Object response)
+    {
+        switch (statusType)
+        {
+        case CertificateStatusType.ocsp:
+            return response instanceof OCSPResponse;
+        default:
+            throw new IllegalArgumentException("'statusType' is an unsupported value");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
new file mode 100644
index 0000000..b947c48
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusRequest.java
@@ -0,0 +1,98 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class CertificateStatusRequest
+{
+    protected short statusType;
+    protected Object request;
+
+    public CertificateStatusRequest(short statusType, Object request)
+    {
+        if (!isCorrectType(statusType, request))
+        {
+            throw new IllegalArgumentException("'request' is not an instance of the correct type");
+        }
+        
+        this.statusType = statusType;
+        this.request = request;
+    }
+
+    public short getStatusType()
+    {
+        return statusType;
+    }
+
+    public Object getRequest()
+    {
+        return request;
+    }
+
+    public OCSPStatusRequest getOCSPStatusRequest()
+    {
+        if (!isCorrectType(CertificateStatusType.ocsp, request))
+        {
+            throw new IllegalStateException("'request' is not an OCSPStatusRequest");
+        }
+        return (OCSPStatusRequest)request;
+    }
+
+    /**
+     * Encode this {@link CertificateStatusRequest} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        TlsUtils.writeUint8(statusType, output);
+
+        switch (statusType)
+        {
+        case CertificateStatusType.ocsp:
+            ((OCSPStatusRequest) request).encode(output);
+            break;
+        default:
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    /**
+     * Parse a {@link CertificateStatusRequest} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link CertificateStatusRequest} object.
+     * @throws IOException
+     */
+    public static CertificateStatusRequest parse(InputStream input) throws IOException
+    {
+        short status_type = TlsUtils.readUint8(input);
+        Object result;
+
+        switch (status_type)
+        {
+        case CertificateStatusType.ocsp:
+            result = OCSPStatusRequest.parse(input);
+            break;
+        default:
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        return new CertificateStatusRequest(status_type, result);
+    }
+
+    protected static boolean isCorrectType(short statusType, Object request)
+    {
+        switch (statusType)
+        {
+        case CertificateStatusType.ocsp:
+            return request instanceof OCSPStatusRequest;
+        default:
+            throw new IllegalArgumentException("'statusType' is an unsupported value");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java
new file mode 100644
index 0000000..bfe8298
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateStatusType.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto.tls;
+
+public class CertificateStatusType
+{
+    /*
+     *  RFC 3546 3.6
+     */
+    public static final short ocsp = 1;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java
new file mode 100644
index 0000000..aab8908
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CertificateURL.java
@@ -0,0 +1,133 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+/*
+ * RFC 3546 3.3
+ */
+public class CertificateURL
+{
+    protected short type;
+    protected Vector urlAndHashList;
+
+    /**
+     * @param type
+     *            see {@link CertChainType} for valid constants.
+     * @param urlAndHashList
+     *            a {@link Vector} of {@link URLAndHash}.
+     */
+    public CertificateURL(short type, Vector urlAndHashList)
+    {
+        if (!CertChainType.isValid(type))
+        {
+            throw new IllegalArgumentException("'type' is not a valid CertChainType value");
+        }
+        if (urlAndHashList == null || urlAndHashList.isEmpty())
+        {
+            throw new IllegalArgumentException("'urlAndHashList' must have length > 0");
+        }
+
+        this.type = type;
+        this.urlAndHashList = urlAndHashList;
+    }
+
+    /**
+     * @return {@link CertChainType}
+     */
+    public short getType()
+    {
+        return type;
+    }
+
+    /**
+     * @return a {@link Vector} of {@link URLAndHash} 
+     */
+    public Vector getURLAndHashList()
+    {
+        return urlAndHashList;
+    }
+
+    /**
+     * Encode this {@link CertificateURL} to an {@link OutputStream}.
+     *
+     * @param output the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output)
+        throws IOException
+    {
+        TlsUtils.writeUint8(this.type, output);
+
+        ListBuffer16 buf = new ListBuffer16();
+        for (int i = 0; i < this.urlAndHashList.size(); ++i)
+        {
+            URLAndHash urlAndHash = (URLAndHash)this.urlAndHashList.elementAt(i);
+            urlAndHash.encode(buf);
+        }
+        buf.encodeTo(output);
+    }
+
+    /**
+     * Parse a {@link CertificateURL} from an {@link InputStream}.
+     * 
+     * @param context
+     *            the {@link TlsContext} of the current connection.
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link CertificateURL} object.
+     * @throws IOException
+     */
+    public static CertificateURL parse(TlsContext context, InputStream input)
+        throws IOException
+    {
+        short type = TlsUtils.readUint8(input);
+        if (!CertChainType.isValid(type))
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        int totalLength = TlsUtils.readUint16(input);
+        if (totalLength < 1)
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        byte[] urlAndHashListData = TlsUtils.readFully(totalLength, input);
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(urlAndHashListData);
+
+        Vector url_and_hash_list = new Vector();
+        while (buf.available() > 0)
+        {
+            URLAndHash url_and_hash = URLAndHash.parse(context, buf);
+            url_and_hash_list.addElement(url_and_hash);
+        }
+
+        return new CertificateURL(type, url_and_hash_list);
+    }
+
+    // TODO Could be more generally useful
+    class ListBuffer16 extends ByteArrayOutputStream
+    {
+        ListBuffer16() throws IOException
+        {
+            // Reserve space for length
+            TlsUtils.writeUint16(0,  this);
+        }
+
+        void encodeTo(OutputStream output) throws IOException
+        {
+            // Patch actual length back in
+            int length = count - 2;
+            TlsUtils.checkUint16(length);
+            TlsUtils.writeUint16(length, buf, 0);
+            output.write(buf, 0, count);
+            buf = null;
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java
new file mode 100644
index 0000000..a858ded
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ChangeCipherSpec.java
@@ -0,0 +1,6 @@
+package org.bouncycastle.crypto.tls;
+
+public class ChangeCipherSpec
+{
+    public static final short change_cipher_spec = 1;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
index 2979cde..c1e7533 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CipherSuite.java
@@ -5,7 +5,6 @@
  */
 public class CipherSuite
 {
-
     public static final int TLS_NULL_WITH_NULL_NULL = 0x0000;
     public static final int TLS_RSA_WITH_NULL_MD5 = 0x0001;
     public static final int TLS_RSA_WITH_NULL_SHA = 0x0002;
@@ -201,7 +200,98 @@
     public static final int TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032;
 
     /*
+     * RFC 5487
+     */
+    public static final int TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8;
+    public static final int TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9;
+    public static final int TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA;
+    public static final int TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB;
+    public static final int TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC;
+    public static final int TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD;
+    public static final int TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE;
+    public static final int TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF;
+    public static final int TLS_PSK_WITH_NULL_SHA256 = 0x00B0;
+    public static final int TLS_PSK_WITH_NULL_SHA384 = 0x00B1;
+    public static final int TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2;
+    public static final int TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3;
+    public static final int TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4;
+    public static final int TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5;
+    public static final int TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6;
+    public static final int TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7;
+    public static final int TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8;
+    public static final int TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9;
+
+    /*
+     * RFC 5489
+     */
+    public static final int TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033;
+    public static final int TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034;
+    public static final int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035;
+    public static final int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036;
+    public static final int TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037;
+    public static final int TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038;
+    public static final int TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039;
+    public static final int TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A;
+    public static final int TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B;
+
+    /*
      * RFC 5746
      */
     public static final int TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF;
+
+    /*
+     * RFC 6655
+     */
+    public static final int TLS_RSA_WITH_AES_128_CCM = 0xC09C;
+    public static final int TLS_RSA_WITH_AES_256_CCM = 0xC09D;
+    public static final int TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E;
+    public static final int TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F;
+    public static final int TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0;
+    public static final int TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1;
+    public static final int TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2;
+    public static final int TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3;
+    public static final int TLS_PSK_WITH_AES_128_CCM = 0xC0A4;
+    public static final int TLS_PSK_WITH_AES_256_CCM = 0xC0A5;
+    public static final int TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6;
+    public static final int TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7;
+    public static final int TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8;
+    public static final int TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9;
+    public static final int TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA;
+    public static final int TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB;
+
+    /*
+     * TBD[draft-josefsson-salsa20-tls-02] 
+     */
+    static final int TLS_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF00;
+    static final int TLS_RSA_WITH_SALSA20_SHA1 = 0xFF01;
+    static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF02;
+    static final int TLS_DHE_RSA_WITH_SALSA20_SHA1 = 0xFF03;
+    static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF04;
+    static final int TLS_ECDHE_RSA_WITH_SALSA20_SHA1 = 0xFF05;
+    static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1 = 0xFF06;
+    static final int TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1 = 0xFF07;
+    static final int TLS_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF08;
+    static final int TLS_PSK_WITH_SALSA20_SHA1 = 0xFF09;
+    static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0A;
+    static final int TLS_DHE_PSK_WITH_SALSA20_SHA1 = 0xFF0B;
+    static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0C;
+    static final int TLS_RSA_PSK_WITH_SALSA20_SHA1 = 0xFF0D;
+    static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1 = 0xFF0E;
+    static final int TLS_ECDHE_PSK_WITH_SALSA20_SHA1 = 0xFF0F;
+    static final int TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF10;
+    static final int TLS_RSA_WITH_SALSA20_UMAC96 = 0xFF11;
+    static final int TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF12;
+    static final int TLS_DHE_RSA_WITH_SALSA20_UMAC96 = 0xFF13;
+    static final int TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF14;
+    static final int TLS_ECDHE_RSA_WITH_SALSA20_UMAC96 = 0xFF15;
+    static final int TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF16;
+    static final int TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96 = 0xFF17;
+    static final int TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF18;
+    static final int TLS_PSK_WITH_SALSA20_UMAC96 = 0xFF19;
+    static final int TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1A;
+    static final int TLS_DHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1B;
+    static final int TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1C;
+    static final int TLS_RSA_PSK_WITH_SALSA20_UMAC96 = 0xFF1D;
+    static final int TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96 = 0xFF1E;
+    static final int TLS_ECDHE_PSK_WITH_SALSA20_UMAC96 = 0xFF1F;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
index 1a48491..43b73bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java
@@ -8,7 +8,6 @@
 class CombinedHash
     implements TlsHandshakeHash
 {
-
     protected TlsContext context;
     protected Digest md5;
     protected Digest sha1;
@@ -31,16 +30,35 @@
         this.context = context;
     }
 
-    public TlsHandshakeHash commit()
+    public TlsHandshakeHash notifyPRFDetermined()
     {
         return this;
     }
 
-    public TlsHandshakeHash fork()
+    public void trackHashAlgorithm(short hashAlgorithm)
+    {
+        throw new IllegalStateException("CombinedHash only supports calculating the legacy PRF for handshake hash");
+    }
+
+    public void sealHashAlgorithms()
+    {
+    }
+
+    public TlsHandshakeHash stopTracking()
     {
         return new CombinedHash(this);
     }
 
+    public Digest forkPRFHash()
+    {
+        return new CombinedHash(this);
+    }
+
+    public byte[] getFinalHash(short hashAlgorithm)
+    {
+        throw new IllegalStateException("CombinedHash doesn't support multiple hashes");
+    }
+
     /**
      * @see org.bouncycastle.crypto.Digest#getAlgorithmName()
      */
@@ -80,7 +98,7 @@
      */
     public int doFinal(byte[] out, int outOff)
     {
-        if (context != null && context.getServerVersion().isSSL())
+        if (context != null && TlsUtils.isSSL(context))
         {
             ssl3Complete(md5, SSL3Mac.IPAD, SSL3Mac.OPAD, 48);
             ssl3Complete(sha1, SSL3Mac.IPAD, SSL3Mac.OPAD, 40);
@@ -102,15 +120,15 @@
 
     protected void ssl3Complete(Digest d, byte[] ipad, byte[] opad, int padLength)
     {
-        byte[] secret = context.getSecurityParameters().masterSecret;
+        byte[] master_secret = context.getSecurityParameters().masterSecret;
 
-        d.update(secret, 0, secret.length);
+        d.update(master_secret, 0, master_secret.length);
         d.update(ipad, 0, padLength);
 
         byte[] tmp = new byte[d.getDigestSize()];
         d.doFinal(tmp, 0);
 
-        d.update(secret, 0, secret.length);
+        d.update(master_secret, 0, master_secret.length);
         d.update(opad, 0, padLength);
         d.update(tmp, 0, tmp.length);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
index d814eac..65ed9b6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ContentType.java
@@ -9,4 +9,5 @@
     public static final short alert = 21;
     public static final short handshake = 22;
     public static final short application_data = 23;
+    public static final short heartbeat = 24;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
index 8ccacfb..b3b1abe 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java
@@ -13,7 +13,6 @@
 public class DTLSClientProtocol
     extends DTLSProtocol
 {
-
     public DTLSClientProtocol(SecureRandom secureRandom)
     {
         super(secureRandom);
@@ -22,7 +21,6 @@
     public DTLSTransport connect(TlsClient client, DatagramTransport transport)
         throws IOException
     {
-
         if (client == null)
         {
             throw new IllegalArgumentException("'client' cannot be null");
@@ -43,6 +41,17 @@
 
         DTLSRecordLayer recordLayer = new DTLSRecordLayer(transport, state.clientContext, client, ContentType.handshake);
 
+        TlsSession sessionToResume = state.client.getSessionToResume();
+        if (sessionToResume != null)
+        {
+            SessionParameters sessionParameters = sessionToResume.exportSessionParameters();
+            if (sessionParameters != null)
+            {
+                state.tlsSession = sessionToResume;
+                state.sessionParameters = sessionParameters;
+            }
+        }
+
         try
         {
             return clientHandshake(state, recordLayer);
@@ -67,7 +76,6 @@
     protected DTLSTransport clientHandshake(ClientHandshakeState state, DTLSRecordLayer recordLayer)
         throws IOException
     {
-
         SecurityParameters securityParameters = state.clientContext.getSecurityParameters();
         DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.clientContext, recordLayer);
 
@@ -76,23 +84,23 @@
 
         DTLSReliableHandshake.Message serverMessage = handshake.receiveMessage();
 
+        while (serverMessage.getType() == HandshakeType.hello_verify_request)
         {
-            // NOTE: After receiving a record from the server, we discover the record layer version
-            ProtocolVersion server_version = recordLayer.getDiscoveredPeerVersion();
+            ProtocolVersion recordLayerVersion = recordLayer.resetDiscoveredPeerVersion();
             ProtocolVersion client_version = state.clientContext.getClientVersion();
 
-            if (!server_version.isEqualOrEarlierVersionOf(client_version))
+            /*
+             * RFC 6347 4.2.1 DTLS 1.2 server implementations SHOULD use DTLS version 1.0 regardless of
+             * the version of TLS that is expected to be negotiated. DTLS 1.2 and 1.0 clients MUST use
+             * the version solely to indicate packet formatting (which is the same in both DTLS 1.2 and
+             * 1.0) and not as part of version negotiation.
+             */
+            if (!recordLayerVersion.isEqualOrEarlierVersionOf(client_version))
             {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
             }
 
-            state.clientContext.setServerVersion(server_version);
-            state.client.notifyServerVersion(server_version);
-        }
-
-        while (serverMessage.getType() == HandshakeType.hello_verify_request)
-        {
-            byte[] cookie = parseHelloVerifyRequest(state.clientContext, serverMessage.getBody());
+            byte[] cookie = processHelloVerifyRequest(state, serverMessage.getBody());
             byte[] patched = patchClientHelloWithCookie(clientHelloBody, cookie);
 
             handshake.resetHandshakeMessagesDigest();
@@ -103,16 +111,24 @@
 
         if (serverMessage.getType() == HandshakeType.server_hello)
         {
+            reportServerVersion(state, recordLayer.getDiscoveredPeerVersion());
+
             processServerHello(state, serverMessage.getBody());
-            serverMessage = handshake.receiveMessage();
         }
         else
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite);
+        if (state.maxFragmentLength >= 0)
+        {
+            int plainTextLimit = 1 << (8 + state.maxFragmentLength);
+            recordLayer.setPlaintextLimit(plainTextLimit);
+        }
+
+        securityParameters.cipherSuite = state.selectedCipherSuite;
         securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
+        securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.clientContext, state.selectedCipherSuite);
 
         /*
          * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
@@ -122,6 +138,48 @@
 
         handshake.notifyHelloComplete();
 
+        boolean resumedSession = state.selectedSessionID.length > 0 && state.tlsSession != null
+            && Arrays.areEqual(state.selectedSessionID, state.tlsSession.getSessionID());
+
+        if (resumedSession)
+        {
+            if (securityParameters.getCipherSuite() != state.sessionParameters.getCipherSuite()
+                || securityParameters.getCompressionAlgorithm() != state.sessionParameters.getCompressionAlgorithm())
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+
+            securityParameters.masterSecret = Arrays.clone(state.sessionParameters.getMasterSecret());
+            recordLayer.initPendingEpoch(state.client.getCipher());
+
+            // NOTE: Calculated exclusive of the actual Finished message from the server
+            byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished,
+                TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
+            processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData);
+
+            // NOTE: Calculated exclusive of the Finished message itself
+            byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished,
+                TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
+            handshake.sendMessage(HandshakeType.finished, clientVerifyData);
+
+            handshake.finish();
+
+            state.clientContext.setResumableSession(state.tlsSession);
+
+            state.client.notifyHandshakeComplete();
+
+            return new DTLSTransport(recordLayer);
+        }
+
+        invalidateSession(state);
+
+        if (state.selectedSessionID.length > 0)
+        {
+            state.tlsSession = new TlsSessionImpl(state.selectedSessionID, null);
+        }
+
+        serverMessage = handshake.receiveMessage();
+
         if (serverMessage.getType() == HandshakeType.supplemental_data)
         {
             processServerSupplementalData(state, serverMessage.getBody());
@@ -135,9 +193,11 @@
         state.keyExchange = state.client.getKeyExchange();
         state.keyExchange.init(state.clientContext);
 
+        Certificate serverCertificate = null;
+
         if (serverMessage.getType() == HandshakeType.certificate)
         {
-            processServerCertificate(state, serverMessage.getBody());
+            serverCertificate = processServerCertificate(state, serverMessage.getBody());
             serverMessage = handshake.receiveMessage();
         }
         else
@@ -146,6 +206,22 @@
             state.keyExchange.skipServerCredentials();
         }
 
+        // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+        if (serverCertificate == null || serverCertificate.isEmpty())
+        {
+            state.allowCertificateStatus = false;
+        }
+
+        if (serverMessage.getType() == HandshakeType.certificate_status)
+        {
+            processCertificateStatus(state, serverMessage.getBody());
+            serverMessage = handshake.receiveMessage();
+        }
+        else
+        {
+            // Okay, CertificateStatus is optional
+        }
+
         if (serverMessage.getType() == HandshakeType.server_key_exchange)
         {
             processServerKeyExchange(state, serverMessage.getBody());
@@ -160,6 +236,14 @@
         if (serverMessage.getType() == HandshakeType.certificate_request)
         {
             processCertificateRequest(state, serverMessage.getBody());
+
+            /*
+             * TODO Give the client a chance to immediately select the CertificateVerify hash
+             * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+             */
+            TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(),
+                state.certificateRequest.getSupportedSignatureAlgorithms());
+
             serverMessage = handshake.receiveMessage();
         }
         else
@@ -179,6 +263,8 @@
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
+        handshake.getHandshakeHash().sealHashAlgorithms();
+
         Vector clientSupplementalData = state.client.getClientSupplementalData();
         if (clientSupplementalData != null)
         {
@@ -223,25 +309,45 @@
         handshake.sendMessage(HandshakeType.client_key_exchange, clientKeyExchangeBody);
 
         TlsProtocol.establishMasterSecret(state.clientContext, state.keyExchange);
+        recordLayer.initPendingEpoch(state.client.getCipher());
 
-        if (state.clientCredentials instanceof TlsSignerCredentials)
+        TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
+
+        if (state.clientCredentials != null && state.clientCredentials instanceof TlsSignerCredentials)
         {
-            /*
-             * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended
-             * from TLS 1.2
-             */
             TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials;
-            byte[] md5andsha1 = handshake.getCurrentHash();
-            byte[] signature = signerCredentials.generateCertificateSignature(md5andsha1);
-            byte[] certificateVerifyBody = generateCertificateVerify(state, signature);
+
+            /*
+             * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+             */
+            SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+            byte[] hash;
+
+            if (TlsUtils.isTLSv12(state.clientContext))
+            {
+                signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
+                if (signatureAndHashAlgorithm == null)
+                {
+                    throw new TlsFatalAlert(AlertDescription.internal_error);
+                }
+
+                hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
+            }
+            else
+            {
+                signatureAndHashAlgorithm = null;
+                hash = TlsProtocol.getCurrentPRFHash(state.clientContext, prepareFinishHash, null);
+            }
+
+            byte[] signature = signerCredentials.generateCertificateSignature(hash);
+            DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
+            byte[] certificateVerifyBody = generateCertificateVerify(state, certificateVerify);
             handshake.sendMessage(HandshakeType.certificate_verify, certificateVerifyBody);
         }
 
-        recordLayer.initPendingEpoch(state.client.getCipher());
-
         // NOTE: Calculated exclusive of the Finished message itself
-        byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "client finished",
-            handshake.getCurrentHash());
+        byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished,
+            TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
         handshake.sendMessage(HandshakeType.finished, clientVerifyData);
 
         if (state.expectSessionTicket)
@@ -258,39 +364,42 @@
         }
 
         // NOTE: Calculated exclusive of the actual Finished message from the server
-        byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, "server finished",
-            handshake.getCurrentHash());
-        serverMessage = handshake.receiveMessage();
-
-        if (serverMessage.getType() == HandshakeType.finished)
-        {
-            processFinished(serverMessage.getBody(), expectedServerVerifyData);
-        }
-        else
-        {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
-        }
+        byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished,
+            TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null));
+        processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData);
 
         handshake.finish();
 
+        if (state.tlsSession != null)
+        {
+            state.sessionParameters = new SessionParameters.Builder()
+                .setCipherSuite(securityParameters.cipherSuite)
+                .setCompressionAlgorithm(securityParameters.compressionAlgorithm)
+                .setMasterSecret(securityParameters.masterSecret)
+                .setPeerCertificate(serverCertificate)
+                .build();
+
+            state.tlsSession = TlsUtils.importSession(state.tlsSession.getSessionID(), state.sessionParameters);
+
+            state.clientContext.setResumableSession(state.tlsSession);
+        }
+
         state.client.notifyHandshakeComplete();
 
         return new DTLSTransport(recordLayer);
     }
 
-    protected byte[] generateCertificateVerify(ClientHandshakeState state, byte[] signature)
+    protected byte[] generateCertificateVerify(ClientHandshakeState state, DigitallySigned certificateVerify)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeOpaque16(signature, buf);
+        certificateVerify.encode(buf);
         return buf.toByteArray();
     }
 
     protected byte[] generateClientHello(ClientHandshakeState state, TlsClient client)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
         ProtocolVersion client_version = client.getClientVersion();
@@ -304,8 +413,17 @@
 
         buf.write(state.clientContext.getSecurityParameters().getClientRandom());
 
-        // Session id
-        TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+        // Session ID
+        byte[] session_id = TlsUtils.EMPTY_BYTES;
+        if (state.tlsSession != null)
+        {
+            session_id = state.tlsSession.getSessionID();
+            if (session_id == null || session_id.length > 32)
+            {
+                session_id = TlsUtils.EMPTY_BYTES;
+            }
+        }
+        TlsUtils.writeOpaque8(session_id, buf);
 
         // Cookie
         TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
@@ -325,32 +443,26 @@
              * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
              * ClientHello. Including both is NOT RECOMMENDED.
              */
-            boolean noRenegExt = state.clientExtensions == null
-                || state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo) == null;
+            byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo);
+            boolean noRenegExt = (null == renegExtData);
 
-            int count = state.offeredCipherSuites.length;
-            if (noRenegExt)
+            boolean noSCSV = !Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+
+            if (noRenegExt && noSCSV)
             {
-                // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
-                ++count;
+                // TODO Consider whether to default to a client extension instead
+                state.offeredCipherSuites = Arrays.append(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
             }
 
-            TlsUtils.writeUint16(2 * count, buf);
-            TlsUtils.writeUint16Array(state.offeredCipherSuites, buf);
-
-            if (noRenegExt)
-            {
-                TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf);
-            }
+            TlsUtils.writeUint16ArrayWithUint16Length(state.offeredCipherSuites, buf);
         }
 
         // TODO Add support for compression
         // Compression methods
         // state.offeredCompressionMethods = client.getCompressionMethods();
-        state.offeredCompressionMethods = new short[]{CompressionMethod._null};
+        state.offeredCompressionMethods = new short[]{ CompressionMethod._null };
 
-        TlsUtils.writeUint8((short)state.offeredCompressionMethods.length, buf);
-        TlsUtils.writeUint8Array(state.offeredCompressionMethods, buf);
+        TlsUtils.writeUint8ArrayWithUint8Length(state.offeredCompressionMethods, buf);
 
         // Extensions
         if (state.clientExtensions != null)
@@ -364,16 +476,29 @@
     protected byte[] generateClientKeyExchange(ClientHandshakeState state)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
         state.keyExchange.generateClientKeyExchange(buf);
         return buf.toByteArray();
     }
 
+    protected void invalidateSession(ClientHandshakeState state)
+    {
+        if (state.sessionParameters != null)
+        {
+            state.sessionParameters.clear();
+            state.sessionParameters = null;
+        }
+
+        if (state.tlsSession != null)
+        {
+            state.tlsSession.invalidate();
+            state.tlsSession = null;
+        }
+    }
+
     protected void processCertificateRequest(ClientHandshakeState state, byte[] body)
         throws IOException
     {
-
         if (state.authentication == null)
         {
             /*
@@ -385,17 +510,67 @@
 
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
-        state.certificateRequest = CertificateRequest.parse(buf);
+        state.certificateRequest = CertificateRequest.parse(state.clientContext, buf);
 
         TlsProtocol.assertEmpty(buf);
 
         state.keyExchange.validateCertificateRequest(state.certificateRequest);
     }
 
+    protected void processCertificateStatus(ClientHandshakeState state, byte[] body)
+        throws IOException
+    {
+        if (!state.allowCertificateStatus)
+        {
+            /*
+             * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
+             * server MUST have included an extension of type "status_request" with empty
+             * "extension_data" in the extended server hello..
+             */
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+        state.certificateStatus = CertificateStatus.parse(buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
+    }
+
+    protected byte[] processHelloVerifyRequest(ClientHandshakeState state, byte[] body)
+        throws IOException
+    {
+        ByteArrayInputStream buf = new ByteArrayInputStream(body);
+
+        ProtocolVersion server_version = TlsUtils.readVersion(buf);
+        byte[] cookie = TlsUtils.readOpaque8(buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        // TODO Seems this behaviour is not yet in line with OpenSSL for DTLS 1.2
+//        reportServerVersion(state, server_version);
+        if (!server_version.isEqualOrEarlierVersionOf(state.clientContext.getClientVersion()))
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        /*
+         * RFC 6347 This specification increases the cookie size limit to 255 bytes for greater
+         * future flexibility. The limit remains 32 for previous versions of DTLS.
+         */
+        if (!ProtocolVersion.DTLSv12.isEqualOrEarlierVersionOf(server_version) && cookie.length > 32)
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        return cookie;
+    }
+
     protected void processNewSessionTicket(ClientHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
@@ -405,10 +580,9 @@
         state.client.notifyNewSessionTicket(newSessionTicket);
     }
 
-    protected void processServerCertificate(ClientHandshakeState state, byte[] body)
+    protected Certificate processServerCertificate(ClientHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         Certificate serverCertificate = Certificate.parse(buf);
@@ -418,34 +592,31 @@
         state.keyExchange.processServerCertificate(serverCertificate);
         state.authentication = state.client.getAuthentication();
         state.authentication.notifyServerCertificate(serverCertificate);
+
+        return serverCertificate;
     }
 
     protected void processServerHello(ClientHandshakeState state, byte[] body)
         throws IOException
     {
-
         SecurityParameters securityParameters = state.clientContext.getSecurityParameters();
 
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
-        // TODO Read RFCs for guidance on the expected record layer version number
         ProtocolVersion server_version = TlsUtils.readVersion(buf);
-        if (!server_version.equals(state.clientContext.getServerVersion()))
-        {
-            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-        }
+        reportServerVersion(state, server_version);
 
         securityParameters.serverRandom = TlsUtils.readFully(32, buf);
 
-        byte[] sessionID = TlsUtils.readOpaque8(buf);
-        if (sessionID.length > 32)
+        state.selectedSessionID = TlsUtils.readOpaque8(buf);
+        if (state.selectedSessionID.length > 32)
         {
             throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
-        state.client.notifySessionID(sessionID);
+        state.client.notifySessionID(state.selectedSessionID);
 
         state.selectedCipherSuite = TlsUtils.readUint16(buf);
-        if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite)
+        if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
             || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
             || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
         {
@@ -457,7 +628,7 @@
         state.client.notifySelectedCipherSuite(state.selectedCipherSuite);
 
         state.selectedCompressionMethod = TlsUtils.readUint8(buf);
-        if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+        if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod))
         {
             throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
@@ -502,7 +673,7 @@
                  * MUST continue to comply with Section 7.4.1.4 for all other extensions.
                  */
                 if (!extType.equals(TlsProtocol.EXT_RenegotiationInfo)
-                    && (state.clientExtensions == null || state.clientExtensions.get(extType) == null))
+                    && null == TlsUtils.getExtensionData(state.clientExtensions, extType))
                 {
                     /*
                      * RFC 3546 2.3 Note that for all extension types (including those defined in
@@ -524,8 +695,8 @@
                  * When a ServerHello is received, the client MUST check if it includes the
                  * "renegotiation_info" extension:
                  */
-                byte[] renegExtValue = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
-                if (renegExtValue != null)
+                byte[] renegExtData = (byte[])serverExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
+                if (renegExtData != null)
                 {
                     /*
                      * If the extension is present, set the secure_renegotiation flag to TRUE. The
@@ -535,7 +706,7 @@
                      */
                     state.secure_renegotiation = true;
 
-                    if (!Arrays.constantTimeAreEqual(renegExtValue,
+                    if (!Arrays.constantTimeAreEqual(renegExtData,
                         TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
                     {
                         throw new TlsFatalAlert(AlertDescription.handshake_failure);
@@ -543,7 +714,16 @@
                 }
             }
 
-            state.expectSessionTicket = serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket);
+            state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, serverExtensions,
+                AlertDescription.illegal_parameter);
+
+            securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(serverExtensions);
+
+            state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(serverExtensions,
+                TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter);
+
+            state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(serverExtensions,
+                TlsProtocol.EXT_SessionTicket, AlertDescription.illegal_parameter);
         }
 
         state.client.notifySecureRenegotiation(state.secure_renegotiation);
@@ -557,7 +737,6 @@
     protected void processServerKeyExchange(ClientHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         state.keyExchange.processServerKeyExchange(buf);
@@ -568,37 +747,30 @@
     protected void processServerSupplementalData(ClientHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
         Vector serverSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
         state.client.processServerSupplementalData(serverSupplementalData);
     }
 
-    protected static byte[] parseHelloVerifyRequest(TlsContext context, byte[] body)
+    protected void reportServerVersion(ClientHandshakeState state, ProtocolVersion server_version)
         throws IOException
     {
-
-        ByteArrayInputStream buf = new ByteArrayInputStream(body);
-
-        ProtocolVersion server_version = TlsUtils.readVersion(buf);
-        if (!server_version.equals(context.getServerVersion()))
+        TlsClientContextImpl clientContext = state.clientContext;
+        ProtocolVersion currentServerVersion = clientContext.getServerVersion();
+        if (null == currentServerVersion)
+        {
+            clientContext.setServerVersion(server_version);
+            state.client.notifyServerVersion(server_version);
+        }
+        else if (!currentServerVersion.equals(server_version))
         {
             throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
-
-        byte[] cookie = TlsUtils.readOpaque8(buf);
-
-        // TODO RFC 4347 has the cookie length restricted to 32, but not in RFC 6347
-
-        TlsProtocol.assertEmpty(buf);
-
-        return cookie;
     }
 
     protected static byte[] patchClientHelloWithCookie(byte[] clientHelloBody, byte[] cookie)
         throws IOException
     {
-
         int sessionIDPos = 34;
         int sessionIDLength = TlsUtils.readUint8(clientHelloBody, sessionIDPos);
 
@@ -607,7 +779,8 @@
 
         byte[] patched = new byte[clientHelloBody.length + cookie.length];
         System.arraycopy(clientHelloBody, 0, patched, 0, cookieLengthPos);
-        TlsUtils.writeUint8((short)cookie.length, patched, cookieLengthPos);
+        TlsUtils.checkUint8(cookie.length);
+        TlsUtils.writeUint8(cookie.length, patched, cookieLengthPos);
         System.arraycopy(cookie, 0, patched, cookiePos, cookie.length);
         System.arraycopy(clientHelloBody, cookiePos, patched, cookiePos + cookie.length, clientHelloBody.length
             - cookiePos);
@@ -619,15 +792,22 @@
     {
         TlsClient client = null;
         TlsClientContextImpl clientContext = null;
+        TlsSession tlsSession = null;
+        SessionParameters sessionParameters = null;
+        SessionParameters.Builder sessionParametersBuilder = null;
         int[] offeredCipherSuites = null;
         short[] offeredCompressionMethods = null;
         Hashtable clientExtensions = null;
+        byte[] selectedSessionID = null;
         int selectedCipherSuite = -1;
         short selectedCompressionMethod = -1;
         boolean secure_renegotiation = false;
+        short maxFragmentLength = -1;
+        boolean allowCertificateStatus = false;
         boolean expectSessionTicket = false;
         TlsKeyExchange keyExchange = null;
         TlsAuthentication authentication = null;
+        CertificateStatus certificateStatus = null;
         CertificateRequest certificateRequest = null;
         TlsCredentials clientCredentials = null;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
index 2789b22..e27580c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSProtocol.java
@@ -4,18 +4,17 @@
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.security.SecureRandom;
+import java.util.Hashtable;
 import java.util.Vector;
 
 import org.bouncycastle.util.Arrays;
 
 public abstract class DTLSProtocol
 {
-
     protected final SecureRandom secureRandom;
 
     protected DTLSProtocol(SecureRandom secureRandom)
     {
-
         if (secureRandom == null)
         {
             throw new IllegalArgumentException("'secureRandom' cannot be null");
@@ -27,7 +26,6 @@
     protected void processFinished(byte[] body, byte[] expected_verify_data)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf);
@@ -40,10 +38,20 @@
         }
     }
 
+    protected static short evaluateMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions,
+        short alertDescription) throws IOException
+    {
+        short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions);
+        if (maxFragmentLength >= 0 && maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions))
+        {
+            throw new TlsFatalAlert(alertDescription);
+        }
+        return maxFragmentLength;
+    }
+
     protected static byte[] generateCertificate(Certificate certificate)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
         certificate.encode(buf);
         return buf.toByteArray();
@@ -52,7 +60,6 @@
     protected static byte[] generateSupplementalData(Vector supplementalData)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
         TlsProtocol.writeSupplementalData(buf, supplementalData);
         return buf.toByteArray();
@@ -61,7 +68,6 @@
     protected static void validateSelectedCipherSuite(int selectedCipherSuite, short alertDescription)
         throws IOException
     {
-
         switch (selectedCipherSuite)
         {
         case CipherSuite.TLS_RSA_EXPORT_WITH_RC4_40_MD5:
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
index 3fde01a..cba13d5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSRecordLayer.java
@@ -5,7 +5,6 @@
 class DTLSRecordLayer
     implements DatagramTransport
 {
-
     private static final int RECORD_HEADER_LENGTH = 13;
     private static final int MAX_FRAGMENT_LENGTH = 1 << 14;
     private static final long TCP_MSL = 1000L * 60 * 2;
@@ -21,6 +20,7 @@
     private volatile boolean failed = false;
     private volatile ProtocolVersion discoveredPeerVersion = null;
     private volatile boolean inHandshake;
+    private volatile int plaintextLimit;
     private DTLSEpoch currentEpoch, pendingEpoch;
     private DTLSEpoch readEpoch, writeEpoch;
 
@@ -40,6 +40,13 @@
         this.pendingEpoch = null;
         this.readEpoch = currentEpoch;
         this.writeEpoch = currentEpoch;
+
+        setPlaintextLimit(MAX_FRAGMENT_LENGTH);
+    }
+
+    void setPlaintextLimit(int plaintextLimit)
+    {
+        this.plaintextLimit = plaintextLimit;
     }
 
     ProtocolVersion getDiscoveredPeerVersion()
@@ -47,6 +54,13 @@
         return discoveredPeerVersion;
     }
 
+    ProtocolVersion resetDiscoveredPeerVersion()
+    {
+        ProtocolVersion result = discoveredPeerVersion; 
+        discoveredPeerVersion = null;
+        return result;
+    }
+
     void initPendingEpoch(TlsCipher pendingCipher)
     {
         if (pendingEpoch != null)
@@ -99,26 +113,24 @@
     public int getReceiveLimit()
         throws IOException
     {
-        return Math.min(MAX_FRAGMENT_LENGTH,
+        return Math.min(this.plaintextLimit,
             readEpoch.getCipher().getPlaintextLimit(transport.getReceiveLimit() - RECORD_HEADER_LENGTH));
     }
 
     public int getSendLimit()
         throws IOException
     {
-        return Math.min(MAX_FRAGMENT_LENGTH,
+        return Math.min(this.plaintextLimit,
             writeEpoch.getCipher().getPlaintextLimit(transport.getSendLimit() - RECORD_HEADER_LENGTH));
     }
 
     public int receive(byte[] buf, int off, int len, int waitMillis)
         throws IOException
     {
-
         byte[] record = null;
 
-        for (; ; )
+        for (;;)
         {
-
             int receiveLimit = Math.min(len, getReceiveLimit()) + RECORD_HEADER_LENGTH;
             if (record == null || record.length < receiveLimit)
             {
@@ -157,6 +169,7 @@
                 case ContentType.application_data:
                 case ContentType.change_cipher_spec:
                 case ContentType.handshake:
+                case ContentType.heartbeat:
                     break;
                 default:
                     // TODO Exception?
@@ -199,6 +212,11 @@
 
                 recordEpoch.getReplayWindow().reportAuthenticated(seq);
 
+                if (plaintext.length > this.plaintextLimit)
+                {
+                    continue;
+                }
+
                 if (discoveredPeerVersion == null)
                 {
                     discoveredPeerVersion = version;
@@ -208,7 +226,6 @@
                 {
                 case ContentType.alert:
                 {
-
                     if (plaintext.length == 2)
                     {
                         short alertLevel = plaintext[0];
@@ -249,14 +266,18 @@
                 {
                     // Implicitly receive change_cipher_spec and change to pending cipher state
 
-                    if (plaintext.length != 1 || plaintext[0] != 1)
+                    for (int i = 0; i < plaintext.length; ++i)
                     {
-                        continue;
-                    }
+                        short message = TlsUtils.readUint8(plaintext, i);
+                        if (message != ChangeCipherSpec.change_cipher_spec)
+                        {
+                            continue;
+                        }
 
-                    if (pendingEpoch != null)
-                    {
-                        readEpoch = pendingEpoch;
+                        if (pendingEpoch != null)
+                        {
+                            readEpoch = pendingEpoch;
+                        }
                     }
 
                     continue;
@@ -273,6 +294,12 @@
                         // TODO Consider support for HelloRequest
                         continue;
                     }
+                    break;
+                }
+                case ContentType.heartbeat:
+                {
+                    // TODO[RFC 6520]
+                    continue;
                 }
                 }
 
@@ -300,18 +327,15 @@
     public void send(byte[] buf, int off, int len)
         throws IOException
     {
-
         short contentType = ContentType.application_data;
 
         if (this.inHandshake || this.writeEpoch == this.retransmitEpoch)
         {
-
             contentType = ContentType.handshake;
 
             short handshakeType = TlsUtils.readUint8(buf, off);
             if (handshakeType == HandshakeType.finished)
             {
-
                 DTLSEpoch nextEpoch = null;
                 if (this.inHandshake)
                 {
@@ -331,7 +355,7 @@
                 // Implicitly send change_cipher_spec and change to pending cipher state
 
                 // TODO Send change_cipher_spec and finished records in single datagram?
-                byte[] data = new byte[]{1};
+                byte[] data = new byte[]{ 1 };
                 sendRecord(ContentType.change_cipher_spec, data, 0, data.length);
 
                 writeEpoch = nextEpoch;
@@ -410,7 +434,6 @@
     private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
         throws IOException
     {
-
         peer.notifyAlertRaised(alertLevel, alertDescription, message, cause);
 
         byte[] error = new byte[2];
@@ -434,8 +457,7 @@
             }
 
             int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length);
-            recordQueue.read(buf, off, received, 0);
-            recordQueue.removeData(received);
+            recordQueue.removeData(buf, off, received, 0);
             return received;
         }
 
@@ -457,6 +479,10 @@
     private void sendRecord(short contentType, byte[] buf, int off, int len)
         throws IOException
     {
+        if (len > this.plaintextLimit)
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
 
         /*
          * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
@@ -473,10 +499,7 @@
         byte[] ciphertext = writeEpoch.getCipher().encodePlaintext(
             getMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len);
 
-        if (ciphertext.length > MAX_FRAGMENT_LENGTH)
-        {
-            throw new TlsFatalAlert(AlertDescription.internal_error);
-        }
+        // TODO Check the ciphertext length?
 
         byte[] record = new byte[ciphertext.length + RECORD_HEADER_LENGTH];
         TlsUtils.writeUint8(contentType, record, 0);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
index 3819251..84ccfcb 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java
@@ -10,12 +10,11 @@
 
 class DTLSReliableHandshake
 {
-
     private final static int MAX_RECEIVE_AHEAD = 10;
 
     private final DTLSRecordLayer recordLayer;
 
-    private TlsHandshakeHash hash = new DeferredHash();
+    private TlsHandshakeHash handshakeHash;
 
     private Hashtable currentInboundFlight = new Hashtable();
     private Hashtable previousInboundFlight = null;
@@ -27,25 +26,31 @@
     DTLSReliableHandshake(TlsContext context, DTLSRecordLayer transport)
     {
         this.recordLayer = transport;
-        this.hash.init(context);
+        this.handshakeHash = new DeferredHash();
+        this.handshakeHash.init(context);
     }
 
     void notifyHelloComplete()
     {
-        this.hash = this.hash.commit();
+        this.handshakeHash = handshakeHash.notifyPRFDetermined();
     }
 
-    byte[] getCurrentHash()
+    TlsHandshakeHash getHandshakeHash()
     {
-        TlsHandshakeHash copyOfHash = hash.fork();
-        byte[] result = new byte[copyOfHash.getDigestSize()];
-        copyOfHash.doFinal(result, 0);
+        return handshakeHash;
+    }
+
+    TlsHandshakeHash prepareToFinish()
+    {
+        TlsHandshakeHash result = handshakeHash;
+        this.handshakeHash = handshakeHash.stopTracking();
         return result;
     }
 
     void sendMessage(short msg_type, byte[] body)
         throws IOException
     {
+        TlsUtils.checkUint24(body.length);
 
         if (!sending)
         {
@@ -62,10 +67,21 @@
         updateHandshakeMessagesDigest(message);
     }
 
+    byte[] receiveMessageBody(short msg_type)
+        throws IOException
+    {
+        Message message = receiveMessage();
+        if (message.getType() != msg_type)
+        {
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
+        }
+
+        return message.getBody();
+    }
+
     Message receiveMessage()
         throws IOException
     {
-
         if (sending)
         {
             sending = false;
@@ -93,7 +109,6 @@
 
         for (; ; )
         {
-
             int receiveLimit = recordLayer.getReceiveLimit();
             if (buf == null || buf.length < receiveLimit)
             {
@@ -280,7 +295,7 @@
 
     void resetHandshakeMessagesDigest()
     {
-        hash.reset();
+        handshakeHash.reset();
     }
 
     /**
@@ -328,8 +343,8 @@
             TlsUtils.writeUint16(message.getSeq(), buf, 4);
             TlsUtils.writeUint24(0, buf, 6);
             TlsUtils.writeUint24(body.length, buf, 9);
-            hash.update(buf, 0, buf.length);
-            hash.update(body, 0, body.length);
+            handshakeHash.update(buf, 0, buf.length);
+            handshakeHash.update(body, 0, body.length);
         }
         return message;
     }
@@ -337,7 +352,6 @@
     private void writeMessage(Message message)
         throws IOException
     {
-
         int sendLimit = recordLayer.getSendLimit();
         int fragmentLimit = sendLimit - 12;
 
@@ -364,18 +378,15 @@
     private void writeHandshakeFragment(Message message, int fragment_offset, int fragment_length)
         throws IOException
     {
+        RecordLayerBuffer fragment = new RecordLayerBuffer(12 + fragment_length);
+        TlsUtils.writeUint8(message.getType(), fragment);
+        TlsUtils.writeUint24(message.getBody().length, fragment);
+        TlsUtils.writeUint16(message.getSeq(), fragment);
+        TlsUtils.writeUint24(fragment_offset, fragment);
+        TlsUtils.writeUint24(fragment_length, fragment);
+        fragment.write(message.getBody(), fragment_offset, fragment_length);
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(message.getType(), buf);
-        TlsUtils.writeUint24(message.getBody().length, buf);
-        TlsUtils.writeUint16(message.getSeq(), buf);
-        TlsUtils.writeUint24(fragment_offset, buf);
-        TlsUtils.writeUint24(fragment_length, buf);
-        buf.write(message.getBody(), fragment_offset, fragment_length);
-
-        byte[] fragment = buf.toByteArray();
-
-        recordLayer.send(fragment, 0, fragment.length);
+        fragment.sendToRecordLayer(recordLayer);
     }
 
     private static boolean checkAll(Hashtable inboundFlight)
@@ -402,7 +413,6 @@
 
     static class Message
     {
-
         private final int message_seq;
         private final short msg_type;
         private final byte[] body;
@@ -429,4 +439,18 @@
             return body;
         }
     }
+
+    static class RecordLayerBuffer extends ByteArrayOutputStream
+    {
+        RecordLayerBuffer(int size)
+        {
+            super(size);
+        }
+
+        void sendToRecordLayer(DTLSRecordLayer recordLayer) throws IOException
+        {
+            recordLayer.send(buf, 0, count);
+            buf = null;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
index 3a100d1..fbb3336 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java
@@ -15,7 +15,6 @@
 public class DTLSServerProtocol
     extends DTLSProtocol
 {
-
     protected boolean verifyRequests = true;
 
     public DTLSServerProtocol(SecureRandom secureRandom)
@@ -36,7 +35,6 @@
     public DTLSTransport accept(TlsServer server, DatagramTransport transport)
         throws IOException
     {
-
         if (server == null)
         {
             throw new IllegalArgumentException("'server' cannot be null");
@@ -80,10 +78,9 @@
         }
     }
 
-    public DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer)
+    protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer)
         throws IOException
     {
-
         SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
         DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer);
 
@@ -105,23 +102,31 @@
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        byte[] serverHelloBody = generateServerHello(state);
-        handshake.sendMessage(HandshakeType.server_hello, serverHelloBody);
-
-        // TODO This block could really be done before actually sending the hello
         {
-            securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.selectedCipherSuite);
+            byte[] serverHelloBody = generateServerHello(state);
+    
+            if (state.maxFragmentLength >= 0)
+            {
+                int plainTextLimit = 1 << (8 + state.maxFragmentLength);
+                recordLayer.setPlaintextLimit(plainTextLimit);
+            }
+    
+            securityParameters.cipherSuite = state.selectedCipherSuite;
             securityParameters.compressionAlgorithm = state.selectedCompressionMethod;
-
+            securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.serverContext,
+                state.selectedCipherSuite);
+    
             /*
              * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length
              * has a verify_data_length equal to 12. This includes all existing cipher suites.
              */
             securityParameters.verifyDataLength = 12;
-
-            handshake.notifyHelloComplete();
+    
+            handshake.sendMessage(HandshakeType.server_hello, serverHelloBody);
         }
 
+        handshake.notifyHelloComplete();
+
         Vector serverSupplementalData = state.server.getServerSupplementalData();
         if (serverSupplementalData != null)
         {
@@ -133,6 +138,9 @@
         state.keyExchange.init(state.serverContext);
 
         state.serverCredentials = state.server.getCredentials();
+
+        Certificate serverCertificate = null;
+
         if (state.serverCredentials == null)
         {
             state.keyExchange.skipServerCredentials();
@@ -141,10 +149,27 @@
         {
             state.keyExchange.processServerCredentials(state.serverCredentials);
 
-            byte[] certificateBody = generateCertificate(state.serverCredentials.getCertificate());
+            serverCertificate = state.serverCredentials.getCertificate();
+            byte[] certificateBody = generateCertificate(serverCertificate);
             handshake.sendMessage(HandshakeType.certificate, certificateBody);
         }
 
+        // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+        if (serverCertificate == null || serverCertificate.isEmpty())
+        {
+            state.allowCertificateStatus = false;
+        }
+
+        if (state.allowCertificateStatus)
+        {
+            CertificateStatus certificateStatus = state.server.getCertificateStatus();
+            if (certificateStatus != null)
+            {
+                byte[] certificateStatusBody = generateCertificateStatus(state, certificateStatus);
+                handshake.sendMessage(HandshakeType.certificate_status, certificateStatusBody);
+            }
+        }
+
         byte[] serverKeyExchange = state.keyExchange.generateServerKeyExchange();
         if (serverKeyExchange != null)
         {
@@ -160,11 +185,16 @@
 
                 byte[] certificateRequestBody = generateCertificateRequest(state, state.certificateRequest);
                 handshake.sendMessage(HandshakeType.certificate_request, certificateRequestBody);
+
+                TlsUtils.trackHashAlgorithms(handshake.getHandshakeHash(),
+                    state.certificateRequest.getSupportedSignatureAlgorithms());
             }
         }
 
         handshake.sendMessage(HandshakeType.server_hello_done, TlsUtils.EMPTY_BYTES);
 
+        handshake.getHandshakeHash().sealHashAlgorithms();
+
         clientMessage = handshake.receiveMessage();
 
         if (clientMessage.getType() == HandshakeType.supplemental_data)
@@ -190,9 +220,7 @@
             }
             else
             {
-                ProtocolVersion equivalentTLSVersion = state.serverContext.getServerVersion().getEquivalentTLSVersion();
-
-                if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion))
+                if (TlsUtils.isTLSv12(state.serverContext))
                 {
                     /*
                      * RFC 5246 If no suitable certificate is available, the client MUST send a
@@ -216,8 +244,11 @@
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
+        TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange);
         recordLayer.initPendingEpoch(state.server.getCipher());
 
+        TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish();
+
         /*
          * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing
          * capability (i.e., all certificates except those containing fixed Diffie-Hellman
@@ -225,33 +256,14 @@
          */
         if (expectCertificateVerifyMessage(state))
         {
-            byte[] certificateVerifyHash = handshake.getCurrentHash();
-            clientMessage = handshake.receiveMessage();
-
-            if (clientMessage.getType() == HandshakeType.certificate_verify)
-            {
-                processCertificateVerify(state, clientMessage.getBody(), certificateVerifyHash);
-            }
-            else
-            {
-                throw new TlsFatalAlert(AlertDescription.unexpected_message);
-            }
+            byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify);
+            processCertificateVerify(state, certificateVerifyBody, prepareFinishHash);
         }
 
         // NOTE: Calculated exclusive of the actual Finished message from the client
-        byte[] clientFinishedHash = handshake.getCurrentHash();
-        clientMessage = handshake.receiveMessage();
-
-        if (clientMessage.getType() == HandshakeType.finished)
-        {
-            byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "client finished",
-                clientFinishedHash);
-            processFinished(clientMessage.getBody(), expectedClientVerifyData);
-        }
-        else
-        {
-            throw new TlsFatalAlert(AlertDescription.unexpected_message);
-        }
+        byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.client_finished,
+            TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null));
+        processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedClientVerifyData);
 
         if (state.expectSessionTicket)
         {
@@ -261,8 +273,8 @@
         }
 
         // NOTE: Calculated exclusive of the Finished message itself
-        byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, "server finished",
-            handshake.getCurrentHash());
+        byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.server_finished,
+            TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null));
         handshake.sendMessage(HandshakeType.finished, serverVerifyData);
 
         handshake.finish();
@@ -275,16 +287,22 @@
     protected byte[] generateCertificateRequest(ServerHandshakeState state, CertificateRequest certificateRequest)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
         certificateRequest.encode(buf);
         return buf.toByteArray();
     }
 
+    protected byte[] generateCertificateStatus(ServerHandshakeState state, CertificateStatus certificateStatus)
+        throws IOException
+    {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        certificateStatus.encode(buf);
+        return buf.toByteArray();
+    }
+
     protected byte[] generateNewSessionTicket(ServerHandshakeState state, NewSessionTicket newSessionTicket)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
         newSessionTicket.encode(buf);
         return buf.toByteArray();
@@ -293,6 +311,7 @@
     protected byte[] generateServerHello(ServerHandshakeState state)
         throws IOException
     {
+        SecurityParameters securityParameters = state.serverContext.getSecurityParameters();
 
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
@@ -310,7 +329,7 @@
 
         TlsUtils.writeVersion(state.serverContext.getServerVersion(), buf);
 
-        buf.write(state.serverContext.getSecurityParameters().serverRandom);
+        buf.write(securityParameters.getServerRandom());
 
         /*
          * The server may return an empty session_id to indicate that the session will not be cached
@@ -319,7 +338,7 @@
         TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
 
         state.selectedCipherSuite = state.server.getSelectedCipherSuite();
-        if (!TlsProtocol.arrayContains(state.offeredCipherSuites, state.selectedCipherSuite)
+        if (!Arrays.contains(state.offeredCipherSuites, state.selectedCipherSuite)
             || state.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
             || state.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
         {
@@ -329,7 +348,7 @@
         validateSelectedCipherSuite(state.selectedCipherSuite, AlertDescription.internal_error);
 
         state.selectedCompressionMethod = state.server.getSelectedCompressionMethod();
-        if (!TlsProtocol.arrayContains(state.offeredCompressionMethods, state.selectedCompressionMethod))
+        if (!Arrays.contains(state.offeredCompressionMethods, state.selectedCompressionMethod))
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
@@ -344,9 +363,8 @@
          */
         if (state.secure_renegotiation)
         {
-
-            boolean noRenegExt = state.serverExtensions == null
-                || !state.serverExtensions.containsKey(TlsProtocol.EXT_RenegotiationInfo);
+            byte[] renegExtData = TlsUtils.getExtensionData(state.serverExtensions, TlsProtocol.EXT_RenegotiationInfo);
+            boolean noRenegExt = (null == renegExtData);
 
             if (noRenegExt)
             {
@@ -357,15 +375,12 @@
                  * because the client is signaling its willingness to receive the extension via the
                  * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
                  */
-                if (state.serverExtensions == null)
-                {
-                    state.serverExtensions = new Hashtable();
-                }
 
                 /*
                  * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                  * "renegotiation_info" extension in the ServerHello message.
                  */
+                state.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(state.serverExtensions);
                 state.serverExtensions.put(TlsProtocol.EXT_RenegotiationInfo,
                     TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
             }
@@ -373,7 +388,17 @@
 
         if (state.serverExtensions != null)
         {
-            state.expectSessionTicket = state.serverExtensions.containsKey(TlsProtocol.EXT_SessionTicket);
+            state.maxFragmentLength = evaluateMaxFragmentLengthExtension(state.clientExtensions, state.serverExtensions,
+                AlertDescription.internal_error);
+
+            securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(state.serverExtensions);
+
+            state.allowCertificateStatus = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions,
+                TlsExtensionsUtils.EXT_status_request, AlertDescription.internal_error);
+
+            state.expectSessionTicket = TlsUtils.hasExpectedEmptyExtensionData(state.serverExtensions,
+                TlsProtocol.EXT_SessionTicket, AlertDescription.internal_error);
+
             TlsProtocol.writeExtensions(buf, state.serverExtensions);
         }
 
@@ -383,7 +408,6 @@
     protected void notifyClientCertificate(ServerHandshakeState state, Certificate clientCertificate)
         throws IOException
     {
-
         if (state.certificateRequest == null)
         {
             throw new IllegalStateException();
@@ -429,7 +453,6 @@
     protected void processClientCertificate(ServerHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         Certificate clientCertificate = Certificate.parse(buf);
@@ -439,27 +462,29 @@
         notifyClientCertificate(state, clientCertificate);
     }
 
-    protected void processCertificateVerify(ServerHandshakeState state, byte[] body, byte[] certificateVerifyHash)
+    protected void processCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
-        byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf);
+        DigitallySigned clientCertificateVerify = DigitallySigned.parse(state.serverContext, buf);
 
         TlsProtocol.assertEmpty(buf);
 
         // Verify the CertificateVerify message contains a correct signature.
         try
         {
-            TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
-            tlsSigner.init(state.serverContext);
+            // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
+            byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null);
 
             org.bouncycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0);
             SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
             AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
 
-            tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, certificateVerifyHash);
+            TlsSigner tlsSigner = TlsUtils.createTlsSigner(state.clientCertificateType);
+            tlsSigner.init(state.serverContext);
+            tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+                clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
         }
         catch (Exception e)
         {
@@ -470,7 +495,6 @@
     protected void processClientHello(ServerHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         // TODO Read RFCs for guidance on the expected record layer version number
@@ -545,7 +569,7 @@
              * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
              * to TRUE.
              */
-            if (TlsProtocol.arrayContains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+            if (Arrays.contains(state.offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
             {
                 state.secure_renegotiation = true;
             }
@@ -554,23 +578,19 @@
              * The server MUST check if the "renegotiation_info" extension is included in the
              * ClientHello.
              */
-            if (state.clientExtensions != null)
+            byte[] renegExtData = TlsUtils.getExtensionData(state.clientExtensions, TlsProtocol.EXT_RenegotiationInfo);
+            if (renegExtData != null)
             {
-                byte[] renegExtValue = (byte[])state.clientExtensions.get(TlsProtocol.EXT_RenegotiationInfo);
-                if (renegExtValue != null)
-                {
-                    /*
-                     * If the extension is present, set secure_renegotiation flag to TRUE. The
-                     * server MUST then verify that the length of the "renegotiated_connection"
-                     * field is zero, and if it is not, MUST abort the handshake.
-                     */
-                    state.secure_renegotiation = true;
+                /*
+                 * If the extension is present, set secure_renegotiation flag to TRUE. The
+                 * server MUST then verify that the length of the "renegotiated_connection"
+                 * field is zero, and if it is not, MUST abort the handshake.
+                 */
+                state.secure_renegotiation = true;
 
-                    if (!Arrays.constantTimeAreEqual(renegExtValue,
-                        TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
-                    {
-                        throw new TlsFatalAlert(AlertDescription.handshake_failure);
-                    }
+                if (!Arrays.constantTimeAreEqual(renegExtData, TlsProtocol.createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+                {
+                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
                 }
             }
         }
@@ -586,20 +606,16 @@
     protected void processClientKeyExchange(ServerHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
 
         state.keyExchange.processClientKeyExchange(buf);
 
         TlsProtocol.assertEmpty(buf);
-
-        TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange);
     }
 
     protected void processClientSupplementalData(ServerHandshakeState state, byte[] body)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(body);
         Vector clientSupplementalData = TlsProtocol.readSupplementalDataMessage(buf);
         state.server.processClientSupplementalData(clientSupplementalData);
@@ -620,6 +636,8 @@
         int selectedCipherSuite = -1;
         short selectedCompressionMethod = -1;
         boolean secure_renegotiation = false;
+        short maxFragmentLength = -1;
+        boolean allowCertificateStatus = false;
         boolean expectSessionTicket = false;
         Hashtable serverExtensions = null;
         TlsKeyExchange keyExchange = null;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
index 98efc4f..78afb41 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsAgreementCredentials.java
@@ -11,9 +11,8 @@
 import org.bouncycastle.util.BigIntegers;
 
 public class DefaultTlsAgreementCredentials
-    implements TlsAgreementCredentials
+    extends AbstractTlsAgreementCredentials
 {
-
     protected Certificate certificate;
     protected AsymmetricKeyParameter privateKey;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
index 82b37d9..7f70c64 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsCipherFactory.java
@@ -4,6 +4,7 @@
 
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.StreamCipher;
 import org.bouncycastle.crypto.digests.MD5Digest;
 import org.bouncycastle.crypto.digests.SHA1Digest;
@@ -15,24 +16,37 @@
 import org.bouncycastle.crypto.engines.DESedeEngine;
 import org.bouncycastle.crypto.engines.RC4Engine;
 import org.bouncycastle.crypto.engines.SEEDEngine;
+import org.bouncycastle.crypto.engines.Salsa20Engine;
+import org.bouncycastle.crypto.macs.HMac;
 import org.bouncycastle.crypto.modes.AEADBlockCipher;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.CCMBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 
 public class DefaultTlsCipherFactory
     extends AbstractTlsCipherFactory
 {
-
     public TlsCipher createCipher(TlsContext context, int encryptionAlgorithm, int macAlgorithm)
         throws IOException
     {
-
         switch (encryptionAlgorithm)
         {
         case EncryptionAlgorithm._3DES_EDE_CBC:
             return createDESedeCipher(context, macAlgorithm);
         case EncryptionAlgorithm.AES_128_CBC:
             return createAESCipher(context, 16, macAlgorithm);
+        case EncryptionAlgorithm.AES_128_CCM:
+            // NOTE: Ignores macAlgorithm
+            return createCipher_AES_CCM(context, 16, 16);
+        case EncryptionAlgorithm.AES_128_CCM_8:
+            // NOTE: Ignores macAlgorithm
+            return createCipher_AES_CCM(context, 16, 8);
+        case EncryptionAlgorithm.AES_256_CCM:
+            // NOTE: Ignores macAlgorithm
+            return createCipher_AES_CCM(context, 32, 16);
+        case EncryptionAlgorithm.AES_256_CCM_8:
+            // NOTE: Ignores macAlgorithm
+            return createCipher_AES_CCM(context, 32, 8);
         case EncryptionAlgorithm.AES_128_GCM:
             // NOTE: Ignores macAlgorithm
             return createCipher_AES_GCM(context, 16, 16);
@@ -45,10 +59,14 @@
             return createCamelliaCipher(context, 16, macAlgorithm);
         case EncryptionAlgorithm.CAMELLIA_256_CBC:
             return createCamelliaCipher(context, 32, macAlgorithm);
+        case EncryptionAlgorithm.ESTREAM_SALSA20:
+            return createSalsa20Cipher(context, 12, 32, macAlgorithm);
         case EncryptionAlgorithm.NULL:
             return createNullCipher(context, macAlgorithm);
         case EncryptionAlgorithm.RC4_128:
             return createRC4Cipher(context, 16, macAlgorithm);
+        case EncryptionAlgorithm.SALSA20:
+            return createSalsa20Cipher(context, 20, 32, macAlgorithm);
         case EncryptionAlgorithm.SEED_CBC:
             return createSEEDCipher(context, macAlgorithm);
         default:
@@ -63,6 +81,13 @@
             createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
     }
 
+    protected TlsAEADCipher createCipher_AES_CCM(TlsContext context, int cipherKeySize, int macSize)
+        throws IOException
+    {
+        return new TlsAEADCipher(context, createAEADBlockCipher_AES_CCM(),
+            createAEADBlockCipher_AES_CCM(), cipherKeySize, macSize);
+    }
+
     protected TlsAEADCipher createCipher_AES_GCM(TlsContext context, int cipherKeySize, int macSize)
         throws IOException
     {
@@ -70,8 +95,7 @@
             createAEADBlockCipher_AES_GCM(), cipherKeySize, macSize);
     }
 
-    protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize,
-                                                  int macAlgorithm)
+    protected TlsBlockCipher createCamelliaCipher(TlsContext context, int cipherKeySize, int macAlgorithm)
         throws IOException
     {
         return new TlsBlockCipher(context, createCamelliaBlockCipher(),
@@ -79,21 +103,6 @@
             createHMACDigest(macAlgorithm), cipherKeySize);
     }
 
-    protected TlsNullCipher createNullCipher(TlsContext context, int macAlgorithm)
-        throws IOException
-    {
-        return new TlsNullCipher(context, createHMACDigest(macAlgorithm),
-            createHMACDigest(macAlgorithm));
-    }
-
-    protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize,
-                                              int macAlgorithm)
-        throws IOException
-    {
-        return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(),
-            createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
-    }
-
     protected TlsBlockCipher createDESedeCipher(TlsContext context, int macAlgorithm)
         throws IOException
     {
@@ -101,6 +110,31 @@
             createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 24);
     }
 
+    protected TlsNullCipher createNullCipher(TlsContext context, int macAlgorithm)
+        throws IOException
+    {
+        return new TlsNullCipher(context, createHMACDigest(macAlgorithm),
+            createHMACDigest(macAlgorithm));
+    }
+
+    protected TlsStreamCipher createRC4Cipher(TlsContext context, int cipherKeySize, int macAlgorithm)
+        throws IOException
+    {
+        return new TlsStreamCipher(context, createRC4StreamCipher(), createRC4StreamCipher(),
+            createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
+    }
+
+    protected TlsStreamCipher createSalsa20Cipher(TlsContext context, int rounds, int cipherKeySize, int macAlgorithm)
+        throws IOException
+    {
+        /*
+         * TODO To be able to support UMAC96, we need to give the TlsStreamCipher a Mac instead of
+         * assuming HMAC and passing a digest.
+         */
+        return new TlsStreamCipher(context, createSalsa20StreamCipher(rounds), createSalsa20StreamCipher(rounds),
+            createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), cipherKeySize);
+    }
+
     protected TlsBlockCipher createSEEDCipher(TlsContext context, int macAlgorithm)
         throws IOException
     {
@@ -108,16 +142,16 @@
             createHMACDigest(macAlgorithm), createHMACDigest(macAlgorithm), 16);
     }
 
-    protected StreamCipher createRC4StreamCipher()
-    {
-        return new RC4Engine();
-    }
-
     protected BlockCipher createAESBlockCipher()
     {
         return new CBCBlockCipher(new AESFastEngine());
     }
 
+    protected AEADBlockCipher createAEADBlockCipher_AES_CCM()
+    {
+        return new CCMBlockCipher(new AESFastEngine());
+    }
+
     protected AEADBlockCipher createAEADBlockCipher_AES_GCM()
     {
         // TODO Consider allowing custom configuration of multiplier
@@ -134,13 +168,22 @@
         return new CBCBlockCipher(new DESedeEngine());
     }
 
+    protected StreamCipher createRC4StreamCipher()
+    {
+        return new RC4Engine();
+    }
+
+    protected StreamCipher createSalsa20StreamCipher(int rounds)
+    {
+        return new Salsa20Engine(rounds);
+    }
+
     protected BlockCipher createSEEDBlockCipher()
     {
         return new CBCBlockCipher(new SEEDEngine());
     }
 
-    protected Digest createHMACDigest(int macAlgorithm)
-        throws IOException
+    protected Digest createHMACDigest(int macAlgorithm) throws IOException
     {
         switch (macAlgorithm)
         {
@@ -160,4 +203,16 @@
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
     }
+
+    protected Mac createMac(int macAlgorithm) throws IOException
+    {
+        switch (macAlgorithm)
+        {
+        // TODO Need an implementation of UMAC
+//        case MACAlgorithm.umac96:
+//            return
+        default:
+            return new HMac(createHMACDigest(macAlgorithm));
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
index 4f9fe27..63db45f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsClient.java
@@ -1,15 +1,10 @@
 package org.bouncycastle.crypto.tls;
 
 import java.io.IOException;
-import java.util.Hashtable;
 
 public abstract class DefaultTlsClient
     extends AbstractTlsClient
 {
-
-    protected int[] namedCurves;
-    protected short[] clientECPointFormats, serverECPointFormats;
-
     public DefaultTlsClient()
     {
         super();
@@ -22,75 +17,15 @@
 
     public int[] getCipherSuites()
     {
-        return new int[]{CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
-            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
-            CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
-            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA,};
-    }
-
-    public Hashtable getClientExtensions()
-        throws IOException
-    {
-
-        Hashtable clientExtensions = super.getClientExtensions();
-
-        if (TlsECCUtils.containsECCCipherSuites(getCipherSuites()))
-        {
-            /*
-             * RFC 4492 5.1. A client that proposes ECC cipher suites in its ClientHello message
-             * appends these extensions (along with any others), enumerating the curves it supports
-             * and the point formats it can parse. Clients SHOULD send both the Supported Elliptic
-             * Curves Extension and the Supported Point Formats Extension.
-             */
-            /*
-             * TODO Could just add all the curves since we support them all, but users may not want
-             * to use unnecessarily large fields. Need configuration options.
-             */
-            this.namedCurves = new int[]{NamedCurve.secp256r1, NamedCurve.sect233r1, NamedCurve.secp224r1,
-                NamedCurve.sect193r1, NamedCurve.secp192r1, NamedCurve.arbitrary_explicit_char2_curves,
-                NamedCurve.arbitrary_explicit_prime_curves};
-            this.clientECPointFormats = new short[]{ECPointFormat.ansiX962_compressed_char2,
-                ECPointFormat.ansiX962_compressed_prime, ECPointFormat.uncompressed};
-
-            if (clientExtensions == null)
-            {
-                clientExtensions = new Hashtable();
-            }
-
-            TlsECCUtils.addSupportedEllipticCurvesExtension(clientExtensions, namedCurves);
-            TlsECCUtils.addSupportedPointFormatsExtension(clientExtensions, clientECPointFormats);
-        }
-
-        return clientExtensions;
-    }
-
-    public void processServerExtensions(Hashtable serverExtensions)
-        throws IOException
-    {
-
-        super.processServerExtensions(serverExtensions);
-
-        if (serverExtensions != null)
-        {
-            int[] namedCurves = TlsECCUtils.getSupportedEllipticCurvesExtension(serverExtensions);
-            if (namedCurves != null)
-            {
-                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-            }
-
-            this.serverECPointFormats = TlsECCUtils.getSupportedPointFormatsExtension(serverExtensions);
-            if (this.serverECPointFormats != null && !TlsECCUtils.isECCCipherSuite(this.selectedCipherSuite))
-            {
-                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-            }
-        }
+        return new int[] { CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
+            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
+            CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256, CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256,
+            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA };
     }
 
     public TlsKeyExchange getKeyExchange()
         throws IOException
     {
-
         switch (selectedCipherSuite)
         {
         case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -132,12 +67,20 @@
         case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
         case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
             return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
 
@@ -170,8 +113,12 @@
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
             return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
 
         case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
@@ -181,24 +128,36 @@
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
         case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
             return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
 
         case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
         case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_RSA_WITH_NULL_MD5:
         case CipherSuite.TLS_RSA_WITH_NULL_SHA:
         case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
         case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
         case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
         case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
             return createRSAKeyExchange();
 
@@ -215,7 +174,6 @@
     public TlsCipher getCipher()
         throws IOException
     {
-
         switch (selectedCipherSuite)
         {
         case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -251,6 +209,14 @@
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null);
+
         case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
@@ -286,6 +252,14 @@
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null);
+
         case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
@@ -311,6 +285,18 @@
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96);
+
         case CipherSuite.TLS_RSA_WITH_NULL_MD5:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5);
 
@@ -334,6 +320,18 @@
         case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96);
+
         case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
         case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
         case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
index a338c38..dcdb493 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsEncryptionCredentials.java
@@ -10,14 +10,14 @@
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 
 public class DefaultTlsEncryptionCredentials
-    implements TlsEncryptionCredentials
+    extends AbstractTlsEncryptionCredentials
 {
     protected TlsContext context;
     protected Certificate certificate;
     protected AsymmetricKeyParameter privateKey;
 
     public DefaultTlsEncryptionCredentials(TlsContext context, Certificate certificate,
-                                           AsymmetricKeyParameter privateKey)
+        AsymmetricKeyParameter privateKey)
     {
         if (certificate == null)
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
index 246b87e..31b6a74 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsServer.java
@@ -48,15 +48,18 @@
     public TlsCredentials getCredentials()
         throws IOException
     {
-
         switch (selectedCipherSuite)
         {
         case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
         case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
@@ -71,9 +74,13 @@
         case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
@@ -85,6 +92,8 @@
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
             return getRSASignerCredentials();
 
         default:
@@ -98,7 +107,6 @@
     public TlsKeyExchange getKeyExchange()
         throws IOException
     {
-
         switch (selectedCipherSuite)
         {
         case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -140,12 +148,20 @@
         case CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA:
         case CipherSuite.TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA:
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
         case CipherSuite.TLS_DHE_RSA_WITH_SEED_CBC_SHA:
             return createDHEKeyExchange(KeyExchangeAlgorithm.DHE_RSA);
 
@@ -178,8 +194,12 @@
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_NULL_SHA:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
             return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_ECDSA);
 
         case CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA:
@@ -189,24 +209,36 @@
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_ECDHE_RSA_WITH_NULL_SHA:
         case CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
             return createECDHEKeyExchange(KeyExchangeAlgorithm.ECDHE_RSA);
 
         case CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
         case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_128_CBC_SHA:
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_RSA_WITH_NULL_MD5:
         case CipherSuite.TLS_RSA_WITH_NULL_SHA:
         case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
         case CipherSuite.TLS_RSA_WITH_RC4_128_MD5:
         case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
         case CipherSuite.TLS_RSA_WITH_SEED_CBC_SHA:
             return createRSAKeyExchange();
 
@@ -221,7 +253,6 @@
     public TlsCipher getCipher()
         throws IOException
     {
-
         switch (selectedCipherSuite)
         {
         case CipherSuite.TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA:
@@ -257,6 +288,14 @@
         case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null);
+
         case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
@@ -292,6 +331,14 @@
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null);
+
         case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
@@ -317,6 +364,18 @@
         case CipherSuite.TLS_RSA_WITH_CAMELLIA_256_CBC_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.CAMELLIA_256_CBC, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_SHA1:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96);
+
         case CipherSuite.TLS_RSA_WITH_NULL_MD5:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_md5);
 
@@ -340,6 +399,18 @@
         case CipherSuite.TLS_RSA_WITH_RC4_128_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_SHA1:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1);
+
+        case CipherSuite.TLS_DHE_RSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_RSA_WITH_SALSA20_UMAC96:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96);
+
         case CipherSuite.TLS_DH_DSS_WITH_SEED_CBC_SHA:
         case CipherSuite.TLS_DH_RSA_WITH_SEED_CBC_SHA:
         case CipherSuite.TLS_DHE_DSS_WITH_SEED_CBC_SHA:
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
index b775250..15bf4a4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DefaultTlsSignerCredentials.java
@@ -9,17 +9,23 @@
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 
 public class DefaultTlsSignerCredentials
-    implements TlsSignerCredentials
+    extends AbstractTlsSignerCredentials
 {
     protected TlsContext context;
     protected Certificate certificate;
     protected AsymmetricKeyParameter privateKey;
+    protected SignatureAndHashAlgorithm signatureAndHashAlgorithm;
 
     protected TlsSigner signer;
 
     public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey)
     {
+        this(context, certificate, privateKey, null);
+    }
 
+    public DefaultTlsSignerCredentials(TlsContext context, Certificate certificate, AsymmetricKeyParameter privateKey,
+        SignatureAndHashAlgorithm signatureAndHashAlgorithm)
+    {
         if (certificate == null)
         {
             throw new IllegalArgumentException("'certificate' cannot be null");
@@ -36,6 +42,10 @@
         {
             throw new IllegalArgumentException("'privateKey' must be private");
         }
+        if (TlsUtils.isTLSv12(context) && signatureAndHashAlgorithm == null)
+        {
+            throw new IllegalArgumentException("'signatureAndHashAlgorithm' cannot be null for (D)TLS 1.2+");
+        }
 
         if (privateKey instanceof RSAKeyParameters)
         {
@@ -59,6 +69,7 @@
         this.context = context;
         this.certificate = certificate;
         this.privateKey = privateKey;
+        this.signatureAndHashAlgorithm = signatureAndHashAlgorithm;
     }
 
     public Certificate getCertificate()
@@ -66,16 +77,28 @@
         return certificate;
     }
 
-    public byte[] generateCertificateSignature(byte[] md5andsha1)
+    public byte[] generateCertificateSignature(byte[] hash)
         throws IOException
     {
         try
         {
-            return signer.generateRawSignature(privateKey, md5andsha1);
+            if (TlsUtils.isTLSv12(context))
+            {
+                return signer.generateRawSignature(signatureAndHashAlgorithm, privateKey, hash);
+            }
+            else
+            {
+                return signer.generateRawSignature(privateKey, hash);
+            }
         }
         catch (CryptoException e)
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
     }
+
+    public SignatureAndHashAlgorithm getSignatureAndHashAlgorithm()
+    {
+        return signatureAndHashAlgorithm;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
index e8c76e6..274e69a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java
@@ -1,8 +1,10 @@
 package org.bouncycastle.crypto.tls;
 
-import java.io.ByteArrayOutputStream;
+import java.util.Enumeration;
+import java.util.Hashtable;
 
 import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.util.Shorts;
 
 /**
  * Buffers input until the hash algorithm is determined.
@@ -10,23 +12,27 @@
 class DeferredHash
     implements TlsHandshakeHash
 {
+    protected static final int BUFFERING_HASH_LIMIT = 4;
 
     protected TlsContext context;
 
-    private ByteArrayOutputStream buf = new ByteArrayOutputStream();
-    private int prfAlgorithm = -1;
-    private Digest hash = null;
+    private DigestInputBuffer buf;
+    private Hashtable hashes;
+    private Short prfHashAlgorithm;
 
     DeferredHash()
     {
-        this.buf = new ByteArrayOutputStream();
-        this.hash = null;
+        this.buf = new DigestInputBuffer();
+        this.hashes = new Hashtable();
+        this.prfHashAlgorithm = null;
     }
 
-    private DeferredHash(Digest hash)
+    private DeferredHash(Short prfHashAlgorithm, Digest prfHash)
     {
         this.buf = null;
-        this.hash = hash;
+        this.hashes = new Hashtable();
+        this.prfHashAlgorithm = prfHashAlgorithm;
+        hashes.put(prfHashAlgorithm, prfHash);
     }
 
     public void init(TlsContext context)
@@ -34,95 +40,168 @@
         this.context = context;
     }
 
-    public TlsHandshakeHash commit()
+    public TlsHandshakeHash notifyPRFDetermined()
     {
-
         int prfAlgorithm = context.getSecurityParameters().getPrfAlgorithm();
-
-        Digest prfHash = TlsUtils.createPRFHash(prfAlgorithm);
-
-        byte[] data = buf.toByteArray();
-        prfHash.update(data, 0, data.length);
-
-        if (prfHash instanceof TlsHandshakeHash)
+        if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
         {
-            TlsHandshakeHash tlsPRFHash = (TlsHandshakeHash)prfHash;
-            tlsPRFHash.init(context);
-            return tlsPRFHash.commit();
+            CombinedHash legacyHash = new CombinedHash();
+            legacyHash.init(context);
+            buf.updateDigest(legacyHash);
+            return legacyHash.notifyPRFDetermined();
         }
 
-        this.prfAlgorithm = prfAlgorithm;
-        this.hash = prfHash;
-        this.buf = null;
+        this.prfHashAlgorithm = Shorts.valueOf(TlsUtils.getHashAlgorithmForPRFAlgorithm(prfAlgorithm));
+
+        checkTrackingHash(prfHashAlgorithm);
 
         return this;
     }
 
-    public TlsHandshakeHash fork()
+    public void trackHashAlgorithm(short hashAlgorithm)
     {
-        checkHash();
-        return new DeferredHash(TlsUtils.clonePRFHash(prfAlgorithm, hash));
+        if (buf == null)
+        {
+            throw new IllegalStateException("Too late to track more hash algorithms");
+        }
+
+        checkTrackingHash(Shorts.valueOf(hashAlgorithm));
+    }
+
+    public void sealHashAlgorithms()
+    {
+        checkStopBuffering();
+    }
+
+    public TlsHandshakeHash stopTracking()
+    {
+        Digest prfHash = TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm));
+        if (buf != null)
+        {
+            buf.updateDigest(prfHash);
+        }
+        DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash);
+        result.init(context);
+        return result;
+    }
+
+    public Digest forkPRFHash()
+    {
+        checkStopBuffering();
+
+        if (buf != null)
+        {
+            Digest prfHash = TlsUtils.createHash(prfHashAlgorithm.shortValue());
+            buf.updateDigest(prfHash);
+            return prfHash;
+        }
+
+        return TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm));
+    }
+
+    public byte[] getFinalHash(short hashAlgorithm)
+    {
+        Digest d = (Digest)hashes.get(Shorts.valueOf(hashAlgorithm));
+        if (d == null)
+        {
+            throw new IllegalStateException("HashAlgorithm " + hashAlgorithm + " is not being tracked");
+        }
+
+        d = TlsUtils.cloneHash(hashAlgorithm, d);
+        if (buf != null)
+        {
+            buf.updateDigest(d);
+        }
+
+        byte[] bs = new byte[d.getDigestSize()];
+        d.doFinal(bs, 0);
+        return bs;
     }
 
     public String getAlgorithmName()
     {
-        checkHash();
-        return hash.getAlgorithmName();
+        throw new IllegalStateException("Use fork() to get a definite Digest");
     }
 
     public int getDigestSize()
     {
-        checkHash();
-        return hash.getDigestSize();
+        throw new IllegalStateException("Use fork() to get a definite Digest");
     }
 
     public void update(byte input)
     {
-        if (hash == null)
+        if (buf != null)
         {
             buf.write(input);
+            return;
         }
-        else
+
+        Enumeration e = hashes.elements();
+        while (e.hasMoreElements())
         {
+            Digest hash = (Digest)e.nextElement();
             hash.update(input);
         }
     }
 
     public void update(byte[] input, int inOff, int len)
     {
-        if (hash == null)
+        if (buf != null)
         {
             buf.write(input, inOff, len);
+            return;
         }
-        else
+
+        Enumeration e = hashes.elements();
+        while (e.hasMoreElements())
         {
+            Digest hash = (Digest)e.nextElement();
             hash.update(input, inOff, len);
         }
     }
 
     public int doFinal(byte[] output, int outOff)
     {
-        checkHash();
-        return hash.doFinal(output, outOff);
+        throw new IllegalStateException("Use fork() to get a definite Digest");
     }
 
     public void reset()
     {
-        if (hash == null)
+        if (buf != null)
         {
             buf.reset();
+            return;
         }
-        else
+
+        Enumeration e = hashes.elements();
+        while (e.hasMoreElements())
         {
+            Digest hash = (Digest)e.nextElement();
             hash.reset();
         }
     }
 
-    protected void checkHash()
+    protected void checkStopBuffering()
     {
-        if (hash == null)
+        if (buf != null && hashes.size() <= BUFFERING_HASH_LIMIT)
         {
-            throw new IllegalStateException("No hash algorithm has been set");
+            Enumeration e = hashes.elements();
+            while (e.hasMoreElements())
+            {
+                Digest hash = (Digest)e.nextElement();
+                buf.updateDigest(hash);
+            }
+
+            this.buf = null;
+        }
+    }
+
+    protected void checkTrackingHash(Short hashAlgorithm)
+    {
+        if (!hashes.containsKey(hashAlgorithm))
+        {
+            Digest hash = TlsUtils.createHash(hashAlgorithm.shortValue());
+            hashes.put(hashAlgorithm, hash);
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java
new file mode 100644
index 0000000..7cfa890
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigestInputBuffer.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Digest;
+
+class DigestInputBuffer extends ByteArrayOutputStream
+{
+    void updateDigest(Digest d)
+    {
+        d.update(this.buf, 0, count);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java
new file mode 100644
index 0000000..c366ca9
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/DigitallySigned.java
@@ -0,0 +1,72 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class DigitallySigned
+{
+    protected SignatureAndHashAlgorithm algorithm;
+    protected byte[] signature;
+
+    public DigitallySigned(SignatureAndHashAlgorithm algorithm, byte[] signature)
+    {
+        if (signature == null)
+        {
+            throw new IllegalArgumentException("'signature' cannot be null");
+        }
+
+        this.algorithm = algorithm;
+        this.signature = signature;
+    }
+
+    /**
+     * @return a {@link SignatureAndHashAlgorithm} (or null before TLS 1.2).
+     */
+    public SignatureAndHashAlgorithm getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public byte[] getSignature()
+    {
+        return signature;
+    }
+
+    /**
+     * Encode this {@link DigitallySigned} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        if (algorithm != null)
+        {
+            algorithm.encode(output);
+        }
+        TlsUtils.writeOpaque16(signature, output);
+    }
+
+    /**
+     * Parse a {@link DigitallySigned} from an {@link InputStream}.
+     * 
+     * @param context
+     *            the {@link TlsContext} of the current connection.
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link DigitallySigned} object.
+     * @throws IOException
+     */
+    public static DigitallySigned parse(TlsContext context, InputStream input) throws IOException
+    {
+        SignatureAndHashAlgorithm algorithm = null;
+        if (TlsUtils.isTLSv12(context))
+        {
+            algorithm = SignatureAndHashAlgorithm.parse(input);
+        }
+        byte[] signature = TlsUtils.readOpaque16(input);
+        return new DigitallySigned(algorithm, signature);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
index f991e4a..6f3fe01 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/EncryptionAlgorithm.java
@@ -25,6 +25,12 @@
     public static final int AES_256_CBC = 9;
 
     /*
+     * RFC 5289
+     */
+    public static final int AES_128_GCM = 10;
+    public static final int AES_256_GCM = 11;
+
+    /*
      * RFC 4132
      */
     public static final int CAMELLIA_128_CBC = 12;
@@ -36,8 +42,16 @@
     public static final int SEED_CBC = 14;
 
     /*
-     * RFC 5289
+     * RFC 6655
      */
-    public static final int AES_128_GCM = 10;
-    public static final int AES_256_GCM = 11;
+    public static final int AES_128_CCM = 15;
+    public static final int AES_128_CCM_8 = 16;
+    public static final int AES_256_CCM = 17;
+    public static final int AES_256_CCM_8 = 18;
+
+    /*
+     * TBD[draft-josefsson-salsa20-tls-02] 
+     */
+    static final int ESTREAM_SALSA20 = 100;
+    static final int SALSA20 = 101;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
index 0be6465..8312e93 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ExtensionType.java
@@ -3,7 +3,7 @@
 public class ExtensionType
 {
     /*
-     * RFC 6066 1.1.
+     * RFC 2546 2.3.
      */
     public static final int server_name = 0;
     public static final int max_fragment_length = 1;
@@ -44,6 +44,11 @@
     public static final int use_srtp = 14;
 
     /*
+     * RFC 6520 6.
+     */
+    public static final int heartbeat = 15;
+
+    /*
      * RFC 5746 3.2.
      */
     public static final int renegotiation_info = 0xff01;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
index 53b4520..c81660a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HandshakeType.java
@@ -17,6 +17,12 @@
     public static final short finished = 20;
 
     /*
+     * RFC 3546 2.4
+     */
+    public static final short certificate_url = 21;
+    public static final short certificate_status = 22;
+
+    /*
      *  (DTLS) RFC 4347 4.3.2
      */
     public static final short hello_verify_request = 3;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
index ac0a4c6..dc7482b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HashAlgorithm.java
@@ -5,7 +5,6 @@
  */
 public class HashAlgorithm
 {
-
     public static final short none = 0;
     public static final short md5 = 1;
     public static final short sha1 = 2;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java
new file mode 100644
index 0000000..f9f3670
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatExtension.java
@@ -0,0 +1,56 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class HeartbeatExtension
+{
+    protected short mode;
+
+    public HeartbeatExtension(short mode)
+    {
+        if (!HeartbeatMode.isValid(mode))
+        {
+            throw new IllegalArgumentException("'mode' is not a valid HeartbeatMode value");
+        }
+
+        this.mode = mode;
+    }
+
+    public short getMode()
+    {
+        return mode;
+    }
+
+    /**
+     * Encode this {@link HeartbeatExtension} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        TlsUtils.writeUint8(mode, output);
+    }
+
+    /**
+     * Parse a {@link HeartbeatExtension} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link HeartbeatExtension} object.
+     * @throws IOException
+     */
+    public static HeartbeatExtension parse(InputStream input) throws IOException
+    {
+        short mode = TlsUtils.readUint8(input);
+        if (!HeartbeatMode.isValid(mode))
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        return new HeartbeatExtension(mode);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
new file mode 100644
index 0000000..320a128
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessage.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.Streams;
+
+public class HeartbeatMessage
+{
+    protected short type;
+    protected byte[] payload;
+    protected int paddingLength;
+
+    public HeartbeatMessage(short type, byte[] payload, int paddingLength)
+    {
+        if (!HeartbeatMessageType.isValid(type))
+        {
+            throw new IllegalArgumentException("'type' is not a valid HeartbeatMessageType value");
+        }
+        if (payload == null || payload.length >= (1 << 16))
+        {
+            throw new IllegalArgumentException("'payload' must have length < 2^16");
+        }
+        if (paddingLength < 16)
+        {
+            throw new IllegalArgumentException("'paddingLength' must be at least 16");
+        }
+
+        this.type = type;
+        this.payload = payload;
+        this.paddingLength = paddingLength;
+    }
+
+    /**
+     * Encode this {@link HeartbeatMessage} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(TlsContext context, OutputStream output) throws IOException
+    {
+        TlsUtils.writeUint8(type, output);
+
+        TlsUtils.checkUint16(payload.length);
+        TlsUtils.writeUint16(payload.length, output);
+        output.write(payload);
+
+        byte[] padding = new byte[paddingLength];
+        context.getSecureRandom().nextBytes(padding);
+        output.write(padding);
+    }
+
+    /**
+     * Parse a {@link HeartbeatMessage} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link HeartbeatMessage} object.
+     * @throws IOException
+     */
+    public static HeartbeatMessage parse(InputStream input) throws IOException
+    {
+        short type = TlsUtils.readUint8(input);
+        if (!HeartbeatMessageType.isValid(type))
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        int payload_length = TlsUtils.readUint16(input);
+
+        PayloadBuffer buf = new PayloadBuffer();
+        Streams.pipeAll(input, buf);
+
+        byte[] payload = buf.toTruncatedByteArray(payload_length);
+        if (payload == null)
+        {
+            /*
+             * RFC 6520 4. If the payload_length of a received HeartbeatMessage is too large, the
+             * received HeartbeatMessage MUST be discarded silently.
+             */
+            return null;
+        }
+
+        int padding_length = buf.size() - payload.length;
+
+        return new HeartbeatMessage(type, payload, padding_length);
+    }
+
+    static class PayloadBuffer extends ByteArrayOutputStream
+    {
+        byte[] toTruncatedByteArray(int payloadLength)
+        {
+            /*
+             * RFC 6520 4. The padding_length MUST be at least 16.
+             */
+            int minimumCount = payloadLength + 16;
+            if (count < minimumCount)
+            {
+                return null;
+            }
+            return Arrays.copyOf(buf, payloadLength);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java
new file mode 100644
index 0000000..f1a3b43
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMessageType.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * RFC 6520 3.
+ */
+public class HeartbeatMessageType
+{
+    public static final short heartbeat_request = 1;
+    public static final short heartbeat_response = 2;
+
+    public static boolean isValid(short heartbeatMessageType)
+    {
+        return heartbeatMessageType >= heartbeat_request && heartbeatMessageType <= heartbeat_response;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java
new file mode 100644
index 0000000..4024faa
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/HeartbeatMode.java
@@ -0,0 +1,15 @@
+package org.bouncycastle.crypto.tls;
+
+/*
+ * RFC 6520
+ */
+public class HeartbeatMode
+{
+    public static final short peer_allowed_to_send = 1;
+    public static final short peer_not_allowed_to_send = 2;
+
+    public static boolean isValid(short heartbeatMode)
+    {
+        return heartbeatMode >= peer_allowed_to_send && heartbeatMode <= peer_not_allowed_to_send;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
index c049bb7..72a944f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/KeyExchangeAlgorithm.java
@@ -44,4 +44,9 @@
     public static final int SRP = 21;
     public static final int SRP_DSS = 22;
     public static final int SRP_RSA = 23;
+    
+    /*
+     * RFC 5489
+     */
+    public static final int ECDHE_PSK = 24;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
index 40ef15c..92adc8c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MACAlgorithm.java
@@ -8,7 +8,6 @@
  */
 public class MACAlgorithm
 {
-
     public static final int _null = 0;
     public static final int md5 = 1;
     public static final int sha = 2;
@@ -21,4 +20,9 @@
     public static final int hmac_sha256 = 3;
     public static final int hmac_sha384 = 4;
     public static final int hmac_sha512 = 5;
+
+    /*
+     * TBD[draft-josefsson-salsa20-tls-02] 
+     */
+    static final int umac96 = 100;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java
new file mode 100644
index 0000000..413695c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/MaxFragmentLength.java
@@ -0,0 +1,17 @@
+package org.bouncycastle.crypto.tls;
+
+public class MaxFragmentLength
+{
+    /*
+     * RFC 3546 3.2.
+     */
+    public static short pow2_9 = 1;
+    public static short pow2_10 = 2;
+    public static short pow2_11 = 3;
+    public static short pow2_12 = 4;
+
+    public static boolean isValid(short maxFragmentLength)
+    {
+        return maxFragmentLength >= pow2_9 && maxFragmentLength <= pow2_12;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java
new file mode 100644
index 0000000..9b0cd1b
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NameType.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.crypto.tls;
+
+public class NameType
+{
+    /*
+     * RFC 3546 3.1.
+     */
+    public static final short host_name = 0;
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
index 690115c..a965d13 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NamedCurve.java
@@ -36,6 +36,13 @@
     public static final int secp256r1 = 23;
     public static final int secp384r1 = 24;
     public static final int secp521r1 = 25;
+    
+    /*
+     * RFC 7027
+     */
+    public static final int brainpoolP256r1 = 26;
+    public static final int brainpoolP384r1 = 27;
+    public static final int brainpoolP512r1 = 28;
 
     /*
      * reserved (0xFE00..0xFEFF)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
index f3d1022..8f87a65 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/NewSessionTicket.java
@@ -6,7 +6,6 @@
 
 public class NewSessionTicket
 {
-
     protected long ticketLifetimeHint;
     protected byte[] ticket;
 
@@ -26,6 +25,12 @@
         return ticket;
     }
 
+    /**
+     * Encode this {@link NewSessionTicket} to an {@link OutputStream}.
+     *
+     * @param output the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
     public void encode(OutputStream output)
         throws IOException
     {
@@ -33,6 +38,13 @@
         TlsUtils.writeOpaque16(ticket, output);
     }
 
+    /**
+     * Parse a {@link NewSessionTicket} from an {@link InputStream}.
+     *
+     * @param input the {@link InputStream} to parse from.
+     * @return a {@link NewSessionTicket} object.
+     * @throws IOException
+     */
     public static NewSessionTicket parse(InputStream input)
         throws IOException
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
new file mode 100644
index 0000000..db8168f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/OCSPStatusRequest.java
@@ -0,0 +1,131 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ocsp.ResponderID;
+import org.bouncycastle.asn1.x509.Extensions;
+
+/**
+ * RFC 3546 3.6
+ */
+public class OCSPStatusRequest
+{
+    protected Vector responderIDList;
+    protected Extensions requestExtensions;
+
+    /**
+     * @param responderIDList
+     *            a {@link Vector} of {@link ResponderID}, specifying the list of trusted OCSP
+     *            responders. An empty list has the special meaning that the responders are
+     *            implicitly known to the server - e.g., by prior arrangement.
+     * @param requestExtensions
+     *            OCSP request extensions. A null value means that there are no extensions.
+     */
+    public OCSPStatusRequest(Vector responderIDList, Extensions requestExtensions)
+    {
+        this.responderIDList = responderIDList;
+        this.requestExtensions = requestExtensions;
+    }
+
+    /**
+     * @return a {@link Vector} of {@link ResponderID}
+     */
+    public Vector getResponderIDList()
+    {
+        return responderIDList;
+    }
+
+    /**
+     * @return OCSP request extensions
+     */
+    public Extensions getRequestExtensions()
+    {
+        return requestExtensions;
+    }
+
+    /**
+     * Encode this {@link OCSPStatusRequest} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        if (responderIDList == null || responderIDList.isEmpty())
+        {
+            TlsUtils.writeUint16(0, output);
+        }
+        else
+        {
+            ByteArrayOutputStream buf = new ByteArrayOutputStream();
+            for (int i = 0; i < responderIDList.size(); ++i)
+            {
+                ResponderID responderID = (ResponderID) responderIDList.elementAt(i);
+                byte[] derEncoding = responderID.getEncoded(ASN1Encoding.DER);
+                TlsUtils.writeOpaque16(derEncoding, buf);
+            }
+            TlsUtils.checkUint16(buf.size());
+            TlsUtils.writeUint16(buf.size(), output);
+            buf.writeTo(output);
+        }
+
+        if (requestExtensions == null)
+        {
+            TlsUtils.writeUint16(0, output);
+        }
+        else
+        {
+            byte[] derEncoding = requestExtensions.getEncoded(ASN1Encoding.DER);
+            TlsUtils.checkUint16(derEncoding.length);
+            TlsUtils.writeUint16(derEncoding.length, output);
+            output.write(derEncoding);
+        }
+    }
+
+    /**
+     * Parse a {@link OCSPStatusRequest} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link OCSPStatusRequest} object.
+     * @throws IOException
+     */
+    public static OCSPStatusRequest parse(InputStream input) throws IOException
+    {
+        Vector responderIDList = new Vector();
+        {
+            int length = TlsUtils.readUint16(input);
+            if (length > 0)
+            {
+                byte[] data = TlsUtils.readFully(length, input);
+                ByteArrayInputStream buf = new ByteArrayInputStream(data);
+                do
+                {
+                    byte[] derEncoding = TlsUtils.readOpaque16(buf);
+                    ResponderID responderID = ResponderID.getInstance(TlsUtils.readDERObject(derEncoding));
+                    responderIDList.addElement(responderID);
+                }
+                while (buf.available() > 0);
+            }
+        }
+
+        Extensions requestExtensions = null;
+        {
+            int length = TlsUtils.readUint16(input);
+            if (length > 0)
+            {
+                byte[] derEncoding = TlsUtils.readFully(length, input);
+                requestExtensions = Extensions.getInstance(TlsUtils.readDERObject(derEncoding));
+            }
+        }
+
+        return new OCSPStatusRequest(responderIDList, requestExtensions);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
index 29750cb..92475b2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/PSKTlsClient.java
@@ -21,41 +21,89 @@
 
     public int[] getCipherSuites()
     {
-        return new int[]{CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA,
-            CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA,
-            CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA, CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA, CipherSuite.TLS_PSK_WITH_RC4_128_SHA,};
+        return new int[] { CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256,
+            CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256,
+            CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA };
     }
 
-    public TlsKeyExchange getKeyExchange()
-        throws IOException
+    public TlsKeyExchange getKeyExchange() throws IOException
     {
-
         switch (selectedCipherSuite)
         {
+        case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+            return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
+
+        case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96:
+            return createPSKKeyExchange(KeyExchangeAlgorithm.ECDHE_PSK);
+
         case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_PSK_WITH_NULL_SHA:
+        case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
         case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_PSK_WITH_SALSA20_UMAC96:
             return createPSKKeyExchange(KeyExchangeAlgorithm.PSK);
 
         case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96:
         case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
+        case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
         case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_UMAC96:
             return createPSKKeyExchange(KeyExchangeAlgorithm.RSA_PSK);
 
-        case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
-            return createPSKKeyExchange(KeyExchangeAlgorithm.DHE_PSK);
-
         default:
             /*
              * Note: internal error here; the TlsProtocol implementation verifies that the
@@ -66,37 +114,114 @@
         }
     }
 
-    public TlsCipher getCipher()
-        throws IOException
+    public TlsCipher getCipher() throws IOException
     {
-
         switch (selectedCipherSuite)
         {
+        case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_PSK_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm._3DES_EDE_CBC, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_128_CBC_SHA256:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CBC, MACAlgorithm.hmac_sha256);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_CCM_8, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_128_GCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA:
         case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CBC, MACAlgorithm.hmac_sha384);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_CCM_8, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.AES_256_GCM, MACAlgorithm._null);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_SHA1:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.hmac_sha1);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_RSA_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.ESTREAM_SALSA20, MACAlgorithm.umac96);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
         case CipherSuite.TLS_PSK_WITH_NULL_SHA:
         case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA256:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha256);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.NULL, MACAlgorithm.hmac_sha384);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
         case CipherSuite.TLS_PSK_WITH_RC4_128_SHA:
         case CipherSuite.TLS_RSA_PSK_WITH_RC4_128_SHA:
-        case CipherSuite.TLS_DHE_PSK_WITH_RC4_128_SHA:
             return cipherFactory.createCipher(context, EncryptionAlgorithm.RC4_128, MACAlgorithm.hmac_sha1);
 
+        case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_SHA1:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.hmac_sha1);
+
+        case CipherSuite.TLS_DHE_PSK_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_PSK_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_RSA_PSK_WITH_SALSA20_UMAC96:
+            return cipherFactory.createCipher(context, EncryptionAlgorithm.SALSA20, MACAlgorithm.umac96);
+
         default:
             /*
              * Note: internal error here; the TlsProtocol implementation verifies that the
@@ -109,6 +234,7 @@
 
     protected TlsKeyExchange createPSKKeyExchange(int keyExchange)
     {
-        return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity);
+        return new TlsPSKKeyExchange(keyExchange, supportedSignatureAlgorithms, pskIdentity, null, namedCurves,
+            clientECPointFormats, serverECPointFormats);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
index c001e58..b32bd9d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ProtocolVersion.java
@@ -4,7 +4,6 @@
 
 public final class ProtocolVersion
 {
-
     public static final ProtocolVersion SSLv3 = new ProtocolVersion(0x0300, "SSL 3.0");
     public static final ProtocolVersion TLSv10 = new ProtocolVersion(0x0301, "TLS 1.0");
     public static final ProtocolVersion TLSv11 = new ProtocolVersion(0x0302, "TLS 1.1");
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
index 3a31c20..cc6640b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java
@@ -12,10 +12,7 @@
  */
 class RecordStream
 {
-
-    private static int PLAINTEXT_LIMIT = (1 << 14);
-    private static int COMPRESSED_LIMIT = PLAINTEXT_LIMIT + 1024;
-    private static int CIPHERTEXT_LIMIT = COMPRESSED_LIMIT + 1024;
+    private static int DEFAULT_PLAINTEXT_LIMIT = (1 << 14);
 
     private TlsProtocol handler;
     private InputStream input;
@@ -26,11 +23,13 @@
     private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
 
     private TlsContext context = null;
-    private TlsHandshakeHash hash = null;
+    private TlsHandshakeHash handshakeHash = null;
 
     private ProtocolVersion readVersion = null, writeVersion = null;
     private boolean restrictReadVersion = true;
 
+    private int plaintextLimit, compressedLimit, ciphertextLimit;
+
     RecordStream(TlsProtocol handler, InputStream input, OutputStream output)
     {
         this.handler = handler;
@@ -40,13 +39,27 @@
         this.writeCompression = this.readCompression;
         this.readCipher = new TlsNullCipher(context);
         this.writeCipher = this.readCipher;
+
+        setPlaintextLimit(DEFAULT_PLAINTEXT_LIMIT);
     }
 
     void init(TlsContext context)
     {
         this.context = context;
-        this.hash = new DeferredHash();
-        this.hash.init(context);
+        this.handshakeHash = new DeferredHash();
+        this.handshakeHash.init(context);
+    }
+
+    int getPlaintextLimit()
+    {
+        return plaintextLimit;
+    }
+
+    void setPlaintextLimit(int plaintextLimit)
+    {
+        this.plaintextLimit = plaintextLimit;
+        this.compressedLimit = this.plaintextLimit + 1024;
+        this.ciphertextLimit = this.compressedLimit + 1024;
     }
 
     ProtocolVersion getReadVersion()
@@ -76,11 +89,6 @@
         this.restrictReadVersion = enabled;
     }
 
-    void notifyHelloComplete()
-    {
-        this.hash = this.hash.commit();
-    }
-
     void setPendingConnectionState(TlsCompression tlsCompression, TlsCipher tlsCipher)
     {
         this.pendingCompression = tlsCompression;
@@ -123,13 +131,17 @@
         pendingCipher = null;
     }
 
-    public void readRecord()
+    public boolean readRecord()
         throws IOException
     {
+        byte[] recordHeader = TlsUtils.readAllOrNothing(5, input);
+        if (recordHeader == null)
+        {
+            return false;
+        }
 
-        short type = TlsUtils.readUint8(input);
+        short type = TlsUtils.readUint8(recordHeader, 0);
 
-        // TODO In earlier RFCs, it was "SHOULD ignore"; should this be version-dependent?
         /*
          * RFC 5246 6. If a TLS implementation receives an unexpected record type, it MUST send an
          * unexpected_message alert.
@@ -138,7 +150,7 @@
 
         if (!restrictReadVersion)
         {
-            int version = TlsUtils.readVersionRaw(input);
+            int version = TlsUtils.readVersionRaw(recordHeader, 1);
             if ((version & 0xffffff00) != 0x0300)
             {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
@@ -146,7 +158,7 @@
         }
         else
         {
-            ProtocolVersion version = TlsUtils.readVersion(input);
+            ProtocolVersion version = TlsUtils.readVersion(recordHeader, 1);
             if (readVersion == null)
             {
                 readVersion = version;
@@ -157,21 +169,21 @@
             }
         }
 
-        int length = TlsUtils.readUint16(input);
+        int length = TlsUtils.readUint16(recordHeader, 3);
         byte[] plaintext = decodeAndVerify(type, input, length);
         handler.processRecord(type, plaintext, 0, plaintext.length);
+        return true;
     }
 
     protected byte[] decodeAndVerify(short type, InputStream input, int len)
         throws IOException
     {
-
-        checkLength(len, CIPHERTEXT_LIMIT, AlertDescription.record_overflow);
+        checkLength(len, ciphertextLimit, AlertDescription.record_overflow);
 
         byte[] buf = TlsUtils.readFully(len, input);
         byte[] decoded = readCipher.decodeCiphertext(readSeqNo++, type, buf, 0, buf.length);
 
-        checkLength(decoded.length, COMPRESSED_LIMIT, AlertDescription.record_overflow);
+        checkLength(decoded.length, compressedLimit, AlertDescription.record_overflow);
 
         /*
          * TODO RFC5264 6.2.2. Implementation note: Decompression functions are responsible for
@@ -190,7 +202,16 @@
          * would decompress to a length in excess of 2^14 bytes, it should report a fatal
          * decompression failure error.
          */
-        checkLength(decoded.length, PLAINTEXT_LIMIT, AlertDescription.decompression_failure);
+        checkLength(decoded.length, plaintextLimit, AlertDescription.decompression_failure);
+
+        /*
+         * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
+         * or ChangeCipherSpec content types.
+         */
+        if (decoded.length < 1 && type != ContentType.application_data)
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
 
         return decoded;
     }
@@ -198,7 +219,6 @@
     protected void writeRecord(short type, byte[] plaintext, int plaintextOffset, int plaintextLength)
         throws IOException
     {
-
         /*
          * RFC 5264 6. Implementations MUST NOT send record types not defined in this document
          * unless negotiated by some extension.
@@ -208,7 +228,7 @@
         /*
          * RFC 5264 6.2.1 The length should not exceed 2^14.
          */
-        checkLength(plaintextLength, PLAINTEXT_LIMIT, AlertDescription.internal_error);
+        checkLength(plaintextLength, plaintextLimit, AlertDescription.internal_error);
 
         /*
          * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert,
@@ -249,7 +269,7 @@
         /*
          * RFC 5264 6.2.3. The length may not exceed 2^14 + 2048.
          */
-        checkLength(ciphertext.length, CIPHERTEXT_LIMIT, AlertDescription.internal_error);
+        checkLength(ciphertext.length, ciphertextLimit, AlertDescription.internal_error);
 
         byte[] record = new byte[ciphertext.length + 5];
         TlsUtils.writeUint8(type, record, 0);
@@ -260,52 +280,44 @@
         output.flush();
     }
 
+    void notifyHelloComplete()
+    {
+        this.handshakeHash = handshakeHash.notifyPRFDetermined();
+    }
+
+    TlsHandshakeHash getHandshakeHash()
+    {
+        return handshakeHash;
+    }
+
+    TlsHandshakeHash prepareToFinish()
+    {
+        TlsHandshakeHash result = handshakeHash;
+        this.handshakeHash = handshakeHash.stopTracking();
+        return result;
+    }
+
     void updateHandshakeData(byte[] message, int offset, int len)
     {
-        hash.update(message, offset, len);
+        handshakeHash.update(message, offset, len);
     }
 
-    /**
-     * 'sender' only relevant to SSLv3
-     */
-    byte[] getCurrentHash(byte[] sender)
+    protected void safeClose()
     {
-        TlsHandshakeHash d = hash.fork();
-
-        if (context.getServerVersion().isSSL())
-        {
-            if (sender != null)
-            {
-                d.update(sender, 0, sender.length);
-            }
-        }
-
-        return doFinal(d);
-    }
-
-    protected void close()
-        throws IOException
-    {
-        IOException e = null;
         try
         {
             input.close();
         }
-        catch (IOException ex)
+        catch (IOException e)
         {
-            e = ex;
         }
+
         try
         {
             output.close();
         }
-        catch (IOException ex)
+        catch (IOException e)
         {
-            e = ex;
-        }
-        if (e != null)
-        {
-            throw e;
         }
     }
 
@@ -322,23 +334,16 @@
         return contents;
     }
 
-    private static byte[] doFinal(Digest d)
-    {
-        byte[] bs = new byte[d.getDigestSize()];
-        d.doFinal(bs, 0);
-        return bs;
-    }
-
     private static void checkType(short type, short alertDescription)
         throws IOException
     {
-
         switch (type)
         {
-        case ContentType.change_cipher_spec:
-        case ContentType.alert:
-        case ContentType.handshake:
         case ContentType.application_data:
+        case ContentType.alert:
+        case ContentType.change_cipher_spec:
+        case ContentType.handshake:
+        case ContentType.heartbeat:
             break;
         default:
             throw new TlsFatalAlert(alertDescription);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
index a5d4840..15295ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SRPTlsClient.java
@@ -1,16 +1,17 @@
 package org.bouncycastle.crypto.tls;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.util.Hashtable;
 
 import org.bouncycastle.util.Arrays;
-import org.bouncycastle.util.Integers;
 
 public abstract class SRPTlsClient
     extends AbstractTlsClient
 {
-    public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp);
+    /**
+     * @deprecated use TlsSRPUtils.EXT_SRP instead
+     */
+    public static final Integer EXT_SRP = TlsSRPUtils.EXT_SRP;
 
     protected byte[] identity;
     protected byte[] password;
@@ -31,40 +32,23 @@
 
     public int[] getCipherSuites()
     {
-        return new int[]{CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA,
-            CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, CipherSuite.TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA,
-            CipherSuite.TLS_SRP_SHA_WITH_AES_256_CBC_SHA, CipherSuite.TLS_SRP_SHA_WITH_AES_128_CBC_SHA,
-            CipherSuite.TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA,};
+        return new int[] { CipherSuite.TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA };
     }
 
     public Hashtable getClientExtensions()
         throws IOException
     {
-
-        Hashtable clientExtensions = super.getClientExtensions();
-        if (clientExtensions == null)
-        {
-            clientExtensions = new Hashtable();
-        }
-
-        ByteArrayOutputStream srpData = new ByteArrayOutputStream();
-        TlsUtils.writeOpaque8(this.identity, srpData);
-        clientExtensions.put(EXT_SRP, srpData.toByteArray());
-
+        Hashtable clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(super.getClientExtensions());
+        TlsSRPUtils.addSRPExtension(clientExtensions, this.identity);
         return clientExtensions;
     }
 
     public void processServerExtensions(Hashtable serverExtensions)
         throws IOException
     {
-        // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
-        if (serverExtensions != null)
+        if (!TlsUtils.hasExpectedEmptyExtensionData(serverExtensions, TlsSRPUtils.EXT_SRP, AlertDescription.illegal_parameter))
         {
-            byte[] extValue = (byte[])serverExtensions.get(EXT_SRP);
-            if (extValue != null && extValue.length > 0)
-            {
-                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-            }
+            // No explicit guidance in RFC 5054 here; we allow an optional empty extension from server
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
index a7701fe..984246e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SecurityParameters.java
@@ -1,16 +1,41 @@
 package org.bouncycastle.crypto.tls;
 
+import org.bouncycastle.util.Arrays;
+
 public class SecurityParameters
 {
-
     int entity = -1;
-    int prfAlgorithm = -1;
+    int cipherSuite = -1;
     short compressionAlgorithm = -1;
+    int prfAlgorithm = -1;
     int verifyDataLength = -1;
     byte[] masterSecret = null;
     byte[] clientRandom = null;
     byte[] serverRandom = null;
 
+    // TODO Keep these internal, since it's maybe not the ideal place for them
+    short maxFragmentLength = -1;
+    boolean truncatedHMac = false;
+
+    void copySessionParametersFrom(SecurityParameters other)
+    {
+        this.entity = other.entity;
+        this.cipherSuite = other.cipherSuite;
+        this.compressionAlgorithm = other.compressionAlgorithm;
+        this.prfAlgorithm = other.prfAlgorithm;
+        this.verifyDataLength = other.verifyDataLength;
+        this.masterSecret = Arrays.clone(other.masterSecret);
+    }
+
+    void clear()
+    {
+        if (this.masterSecret != null)
+        {
+            Arrays.fill(this.masterSecret, (byte)0);
+            this.masterSecret = null;
+        }
+    }
+
     /**
      * @return {@link ConnectionEnd}
      */
@@ -20,11 +45,11 @@
     }
 
     /**
-     * @return {@link PRFAlgorithm}
+     * @return {@link CipherSuite}
      */
-    public int getPrfAlgorithm()
+    public int getCipherSuite()
     {
-        return prfAlgorithm;
+        return cipherSuite;
     }
 
     /**
@@ -35,6 +60,14 @@
         return compressionAlgorithm;
     }
 
+    /**
+     * @return {@link PRFAlgorithm}
+     */
+    public int getPrfAlgorithm()
+    {
+        return prfAlgorithm;
+    }
+
     public int getVerifyDataLength()
     {
         return verifyDataLength;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
new file mode 100644
index 0000000..c3050f1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerDHParams.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.math.BigInteger;
+
+import org.bouncycastle.crypto.params.DHParameters;
+import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+
+public class ServerDHParams
+{
+    protected DHPublicKeyParameters publicKey;
+
+    public ServerDHParams(DHPublicKeyParameters publicKey)
+    {
+        if (publicKey == null)
+        {
+            throw new IllegalArgumentException("'publicKey' cannot be null");
+        }
+
+        this.publicKey = publicKey;
+    }
+
+    public DHPublicKeyParameters getPublicKey()
+    {
+        return publicKey;
+    }
+
+    /**
+     * Encode this {@link ServerDHParams} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        DHParameters dhParameters = publicKey.getParameters();
+        BigInteger Ys = publicKey.getY();
+
+        TlsDHUtils.writeDHParameter(dhParameters.getP(), output);
+        TlsDHUtils.writeDHParameter(dhParameters.getG(), output);
+        TlsDHUtils.writeDHParameter(Ys, output);
+    }
+
+    /**
+     * Parse a {@link ServerDHParams} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link ServerDHParams} object.
+     * @throws IOException
+     */
+    public static ServerDHParams parse(InputStream input) throws IOException
+    {
+        BigInteger p = TlsDHUtils.readDHParameter(input);
+        BigInteger g = TlsDHUtils.readDHParameter(input);
+        BigInteger Ys = TlsDHUtils.readDHParameter(input);
+
+        return new ServerDHParams(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
new file mode 100644
index 0000000..df9a439
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerName.java
@@ -0,0 +1,112 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Strings;
+
+public class ServerName
+{
+    protected short nameType;
+    protected Object name;
+
+    public ServerName(short nameType, Object name)
+    {
+        if (!isCorrectType(nameType, name))
+        {
+            throw new IllegalArgumentException("'name' is not an instance of the correct type");
+        }
+
+        this.nameType = nameType;
+        this.name = name;
+    }
+
+    public short getNameType()
+    {
+        return nameType;
+    }
+
+    public Object getName()
+    {
+        return name;
+    }
+
+    public String getHostName()
+    {
+        if (!isCorrectType(NameType.host_name, name))
+        {
+            throw new IllegalStateException("'name' is not a HostName string");
+        }
+        return (String)name;
+    }
+
+    /**
+     * Encode this {@link ServerName} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        TlsUtils.writeUint8(nameType, output);
+
+        switch (nameType)
+        {
+        case NameType.host_name:
+            byte[] utf8Encoding = Strings.toUTF8ByteArray((String)name);
+            if (utf8Encoding.length < 1)
+            {
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+            TlsUtils.writeOpaque16(utf8Encoding, output);
+            break;
+        default:
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    /**
+     * Parse a {@link ServerName} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link ServerName} object.
+     * @throws IOException
+     */
+    public static ServerName parse(InputStream input) throws IOException
+    {
+        short name_type = TlsUtils.readUint8(input);
+        Object name;
+
+        switch (name_type)
+        {
+        case NameType.host_name:
+        {
+            byte[] utf8Encoding = TlsUtils.readOpaque16(input);
+            if (utf8Encoding.length < 1)
+            {
+                throw new TlsFatalAlert(AlertDescription.decode_error);
+            }
+            name = Strings.fromUTF8ByteArray(utf8Encoding);
+            break;
+        }
+        default:
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        return new ServerName(name_type, name);
+    }
+
+    protected static boolean isCorrectType(short nameType, Object name)
+    {
+        switch (nameType)
+        {
+        case NameType.host_name:
+            return name instanceof String;
+        default:
+            throw new IllegalArgumentException("'name' is an unsupported value");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java
new file mode 100644
index 0000000..1dc81f0
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/ServerNameList.java
@@ -0,0 +1,86 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Vector;
+
+public class ServerNameList
+{
+    protected Vector serverNameList;
+
+    /**
+     * @param serverNameList a {@link Vector} of {@link ServerName}.
+     */
+    public ServerNameList(Vector serverNameList)
+    {
+        if (serverNameList == null || serverNameList.isEmpty())
+        {
+            throw new IllegalArgumentException("'serverNameList' must not be null or empty");
+        }
+
+        this.serverNameList = serverNameList;
+    }
+
+    /**
+     * @return a {@link Vector} of {@link ServerName}.
+     */
+    public Vector getServerNameList()
+    {
+        return serverNameList;
+    }
+
+    /**
+     * Encode this {@link ServerNameList} to an {@link OutputStream}.
+     * 
+     * @param output
+     *            the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output) throws IOException
+    {
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+        for (int i = 0; i < serverNameList.size(); ++i)
+        {
+            ServerName entry = (ServerName)serverNameList.elementAt(i);
+            entry.encode(buf);
+        }
+
+        TlsUtils.checkUint16(buf.size());
+        TlsUtils.writeUint16(buf.size(), output);
+        buf.writeTo(output);
+    }
+
+    /**
+     * Parse a {@link ServerNameList} from an {@link InputStream}.
+     * 
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link ServerNameList} object.
+     * @throws IOException
+     */
+    public static ServerNameList parse(InputStream input) throws IOException
+    {
+        int length = TlsUtils.readUint16(input);
+        if (length < 1)
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        byte[] data = TlsUtils.readFully(length, input);
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(data);
+
+        Vector server_name_list = new Vector();
+        while (buf.available() > 0)
+        {
+            ServerName entry = ServerName.parse(buf);
+            server_name_list.addElement(entry);
+        }
+
+        return new ServerNameList(server_name_list);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
new file mode 100644
index 0000000..68412f8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SessionParameters.java
@@ -0,0 +1,142 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Arrays;
+
+public final class SessionParameters
+{
+    public static final class Builder
+    {
+        private int cipherSuite = -1;
+        private short compressionAlgorithm = -1;
+        private byte[] masterSecret = null;
+        private Certificate peerCertificate = null;
+        private byte[] encodedServerExtensions = null;
+
+        public Builder()
+        {
+        }
+
+        public SessionParameters build()
+        {
+            validate(this.cipherSuite >= 0, "cipherSuite");
+            validate(this.compressionAlgorithm >= 0, "compressionAlgorithm");
+            validate(this.masterSecret != null, "masterSecret");
+            return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate,
+                encodedServerExtensions);
+        }
+
+        public Builder setCipherSuite(int cipherSuite)
+        {
+            this.cipherSuite = cipherSuite;
+            return this;
+        }
+
+        public Builder setCompressionAlgorithm(short compressionAlgorithm)
+        {
+            this.compressionAlgorithm = compressionAlgorithm;
+            return this;
+        }
+
+        public Builder setMasterSecret(byte[] masterSecret)
+        {
+            this.masterSecret = masterSecret;
+            return this;
+        }
+
+        public Builder setPeerCertificate(Certificate peerCertificate)
+        {
+            this.peerCertificate = peerCertificate;
+            return this;
+        }
+
+        public Builder setServerExtensions(Hashtable serverExtensions)
+            throws IOException
+        {
+            if (serverExtensions == null)
+            {
+                encodedServerExtensions = null;
+            }
+            else
+            {
+                ByteArrayOutputStream buf = new ByteArrayOutputStream();
+                TlsProtocol.writeExtensions(buf, serverExtensions);
+                encodedServerExtensions = buf.toByteArray();
+            }
+            return this;
+        }
+
+        private void validate(boolean condition, String parameter)
+        {
+            if (!condition)
+            {
+                throw new IllegalStateException("Required session parameter '" + parameter + "' not configured");
+            }
+        }
+    }
+
+    private int cipherSuite;
+    private short compressionAlgorithm;
+    private byte[] masterSecret;
+    private Certificate peerCertificate;
+    private byte[] encodedServerExtensions;
+
+    private SessionParameters(int cipherSuite, short compressionAlgorithm, byte[] masterSecret,
+        Certificate peerCertificate, byte[] encodedServerExtensions)
+    {
+        this.cipherSuite = cipherSuite;
+        this.compressionAlgorithm = compressionAlgorithm;
+        this.masterSecret = Arrays.clone(masterSecret);
+        this.peerCertificate = peerCertificate;
+        this.encodedServerExtensions = encodedServerExtensions;
+    }
+
+    public void clear()
+    {
+        if (this.masterSecret != null)
+        {
+            Arrays.fill(this.masterSecret, (byte)0);
+        }
+    }
+
+    public SessionParameters copy()
+    {
+        return new SessionParameters(cipherSuite, compressionAlgorithm, masterSecret, peerCertificate,
+            encodedServerExtensions);
+    }
+
+    public int getCipherSuite()
+    {
+        return cipherSuite;
+    }
+
+    public short getCompressionAlgorithm()
+    {
+        return compressionAlgorithm;
+    }
+
+    public byte[] getMasterSecret()
+    {
+        return masterSecret;
+    }
+
+    public Certificate getPeerCertificate()
+    {
+        return peerCertificate;
+    }
+
+    public Hashtable readServerExtensions() throws IOException
+    {
+        if (encodedServerExtensions == null)
+        {
+            return null;
+        }
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(encodedServerExtensions);
+        return TlsProtocol.readExtensions(buf);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
index 7ad4644..a5a591e 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignatureAndHashAlgorithm.java
@@ -9,9 +9,8 @@
  */
 public class SignatureAndHashAlgorithm
 {
-
-    private short hash;
-    private short signature;
+    protected short hash;
+    protected short signature;
 
     /**
      * @param hash      {@link HashAlgorithm}
@@ -19,7 +18,6 @@
      */
     public SignatureAndHashAlgorithm(short hash, short signature)
     {
-
         if (!TlsUtils.isValidUint8(hash))
         {
             throw new IllegalArgumentException("'hash' should be a uint8");
@@ -65,7 +63,7 @@
 
     public int hashCode()
     {
-        return (getHash() << 8) | getSignature();
+        return (getHash() << 16) | getSignature();
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java
new file mode 100644
index 0000000..8293135
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SignerInputBuffer.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayOutputStream;
+
+import org.bouncycastle.crypto.Signer;
+
+class SignerInputBuffer extends ByteArrayOutputStream
+{
+    void updateSigner(Signer s)
+    {
+        s.update(this.buf, 0, count);
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
index 5a71f9b..4080aaa 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/SupplementalDataEntry.java
@@ -2,19 +2,18 @@
 
 public class SupplementalDataEntry
 {
+    protected int dataType;
+    protected byte[] data;
 
-    private int supp_data_type;
-    private byte[] data;
-
-    public SupplementalDataEntry(int supp_data_type, byte[] data)
+    public SupplementalDataEntry(int dataType, byte[] data)
     {
-        this.supp_data_type = supp_data_type;
+        this.dataType = dataType;
         this.data = data;
     }
 
     public int getDataType()
     {
-        return supp_data_type;
+        return dataType;
     }
 
     public byte[] getData()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
index dbf9d79..bb9306a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsAEADCipher.java
@@ -10,7 +10,6 @@
 public class TlsAEADCipher
     implements TlsCipher
 {
-
     protected TlsContext context;
     protected int macSize;
     protected int nonce_explicit_length;
@@ -21,11 +20,9 @@
     protected byte[] encryptImplicitNonce, decryptImplicitNonce;
 
     public TlsAEADCipher(TlsContext context, AEADBlockCipher clientWriteCipher, AEADBlockCipher serverWriteCipher,
-                         int cipherKeySize, int macSize)
-        throws IOException
+        int cipherKeySize, int macSize) throws IOException
     {
-
-        if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()))
+        if (!TlsUtils.isTLSv12(context))
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
@@ -33,7 +30,7 @@
         this.context = context;
         this.macSize = macSize;
 
-        // NOTE: Valid for RFC 5288 ciphers but may need review for other AEAD ciphers
+        // NOTE: Valid for RFC 5288/6655 ciphers but may need review for other AEAD ciphers
         this.nonce_explicit_length = 8;
 
         // TODO SecurityParameters.fixed_iv_length
@@ -94,12 +91,11 @@
     public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
         throws IOException
     {
-
         byte[] nonce = new byte[this.encryptImplicitNonce.length + nonce_explicit_length];
         System.arraycopy(encryptImplicitNonce, 0, nonce, 0, encryptImplicitNonce.length);
 
         /*
-         * RFC 5288 The nonce_explicit MAY be the 64-bit sequence number.
+         * RFC 5288/6655 The nonce_explicit MAY be the 64-bit sequence number.
          * 
          * (May need review for other AEAD ciphers).
          */
@@ -113,12 +109,13 @@
         System.arraycopy(nonce, encryptImplicitNonce.length, output, 0, nonce_explicit_length);
         int outputPos = nonce_explicit_length;
 
-        encryptCipher.init(true,
-            new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength)));
+        byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength);
+        AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData);
 
-        outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
         try
         {
+            encryptCipher.init(true, parameters);
+            outputPos += encryptCipher.processBytes(plaintext, plaintextOffset, plaintextLength, output, outputPos);
             outputPos += encryptCipher.doFinal(output, outputPos);
         }
         catch (Exception e)
@@ -138,7 +135,6 @@
     public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
         throws IOException
     {
-
         if (getPlaintextLimit(len) < 0)
         {
             throw new TlsFatalAlert(AlertDescription.decode_error);
@@ -155,13 +151,13 @@
         byte[] output = new byte[plaintextLength];
         int outputPos = 0;
 
-        decryptCipher.init(false,
-            new AEADParameters(null, 8 * macSize, nonce, getAdditionalData(seqNo, type, plaintextLength)));
-
-        outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
+        byte[] additionalData = getAdditionalData(seqNo, type, plaintextLength);
+        AEADParameters parameters = new AEADParameters(null, 8 * macSize, nonce, additionalData);
 
         try
         {
+            decryptCipher.init(false, parameters);
+            outputPos += decryptCipher.processBytes(ciphertext, ciphertextOffset, ciphertextLength, output, outputPos);
             outputPos += decryptCipher.doFinal(output, outputPos);
         }
         catch (Exception e)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
index 0b218c1..2f9e8a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsBlockCipher.java
@@ -16,6 +16,7 @@
 public class TlsBlockCipher
     implements TlsCipher
 {
+    private static boolean encryptThenMAC = false;
 
     protected TlsContext context;
     protected byte[] randomData;
@@ -38,17 +39,14 @@
     }
 
     public TlsBlockCipher(TlsContext context, BlockCipher clientWriteCipher, BlockCipher serverWriteCipher,
-                          Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize)
-        throws IOException
+        Digest clientWriteDigest, Digest serverWriteDigest, int cipherKeySize) throws IOException
     {
-
         this.context = context;
 
         this.randomData = new byte[256];
         context.getSecureRandom().nextBytes(randomData);
 
-        this.useExplicitIV = ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion()
-            .getEquivalentTLSVersion());
+        this.useExplicitIV = TlsUtils.isTLSv11(context);
 
         int key_block_size = (2 * cipherKeySize) + clientWriteDigest.getDigestSize()
             + serverWriteDigest.getDigestSize();
@@ -123,13 +121,30 @@
         int blockSize = encryptCipher.getBlockSize();
         int macSize = writeMac.getSize();
 
-        int result = ciphertextLimit - (ciphertextLimit % blockSize) - macSize - 1;
+        int plaintextLimit = ciphertextLimit;
+
+        // An explicit IV consumes 1 block
         if (useExplicitIV)
         {
-            result -= blockSize;
+            plaintextLimit -= blockSize;
         }
 
-        return result;
+        // Leave room for the MAC, and require block-alignment
+        if (encryptThenMAC)
+        {
+            plaintextLimit -= macSize;
+            plaintextLimit -= plaintextLimit % blockSize;
+        }
+        else
+        {
+            plaintextLimit -= plaintextLimit % blockSize;
+            plaintextLimit -= macSize;
+        }
+
+        // Minimum 1 byte of padding
+        --plaintextLimit;
+
+        return plaintextLimit;
     }
 
     public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
@@ -139,7 +154,13 @@
 
         ProtocolVersion version = context.getServerVersion();
 
-        int padding_length = blockSize - 1 - ((len + macSize) % blockSize);
+        int enc_input_length = len;
+        if (!encryptThenMAC)
+        {
+            enc_input_length += macSize;
+        }
+
+        int padding_length = blockSize - 1 - (enc_input_length % blockSize);
 
         // TODO[DTLS] Consider supporting in DTLS (without exceeding send limit though)
         if (!version.isDTLS() && !version.isSSL())
@@ -156,7 +177,7 @@
             totalSize += blockSize;
         }
 
-        byte[] outbuf = new byte[totalSize];
+        byte[] outBuf = new byte[totalSize];
         int outOff = 0;
 
         if (useExplicitIV)
@@ -166,25 +187,42 @@
 
             encryptCipher.init(true, new ParametersWithIV(null, explicitIV));
 
-            System.arraycopy(explicitIV, 0, outbuf, outOff, blockSize);
+            System.arraycopy(explicitIV, 0, outBuf, outOff, blockSize);
             outOff += blockSize;
         }
 
-        byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+        int blocks_start = outOff;
 
-        System.arraycopy(plaintext, offset, outbuf, outOff, len);
-        System.arraycopy(mac, 0, outbuf, outOff + len, mac.length);
+        System.arraycopy(plaintext, offset, outBuf, outOff, len);
+        outOff += len;
 
-        int padOffset = outOff + len + mac.length;
+        if (!encryptThenMAC)
+        {
+            byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+            System.arraycopy(mac, 0, outBuf, outOff, mac.length);
+            outOff += mac.length;
+        }
+
         for (int i = 0; i <= padding_length; i++)
         {
-            outbuf[i + padOffset] = (byte)padding_length;
+            outBuf[outOff++] = (byte)padding_length;
         }
-        for (int i = outOff; i < totalSize; i += blockSize)
+
+        for (int i = blocks_start; i < outOff; i += blockSize)
         {
-            encryptCipher.processBlock(outbuf, i, outbuf, i);
+            encryptCipher.processBlock(outBuf, i, outBuf, i);
         }
-        return outbuf;
+
+        if (encryptThenMAC)
+        {
+            byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, outOff);
+            System.arraycopy(mac, 0, outBuf, outOff, mac.length);
+            outOff += mac.length;
+        }
+
+//        assert outBuf.length == outOff;
+
+        return outBuf;
     }
 
     public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
@@ -193,7 +231,16 @@
         int blockSize = decryptCipher.getBlockSize();
         int macSize = readMac.getSize();
 
-        int minLen = Math.max(blockSize, macSize + 1);
+        int minLen = blockSize;
+        if (encryptThenMAC)
+        {
+            minLen += macSize;
+        }
+        else
+        {
+            minLen = Math.max(minLen, macSize + 1);
+        }
+
         if (useExplicitIV)
         {
             minLen += blockSize;
@@ -204,41 +251,67 @@
             throw new TlsFatalAlert(AlertDescription.decode_error);
         }
 
-        if (len % blockSize != 0)
+        int blocks_length = len;
+        if (encryptThenMAC)
+        {
+            blocks_length -= macSize;
+        }
+
+        if (blocks_length % blockSize != 0)
         {
             throw new TlsFatalAlert(AlertDescription.decryption_failed);
         }
 
+        if (encryptThenMAC)
+        {
+            int end = offset + len;
+            byte[] receivedMac = Arrays.copyOfRange(ciphertext, end - macSize, end);
+            byte[] calculatedMac = readMac.calculateMac(seqNo, type, ciphertext, offset, len - macSize);
+
+            boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
+
+            if (badMac)
+            {
+                throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+            }
+        }
+
         if (useExplicitIV)
         {
             decryptCipher.init(false, new ParametersWithIV(null, ciphertext, offset, blockSize));
 
             offset += blockSize;
-            len -= blockSize;
+            blocks_length -= blockSize;
         }
 
-        for (int i = 0; i < len; i += blockSize)
+        for (int i = 0; i < blocks_length; i += blockSize)
         {
             decryptCipher.processBlock(ciphertext, offset + i, ciphertext, offset + i);
         }
 
         // If there's anything wrong with the padding, this will return zero
-        int totalPad = checkPaddingConstantTime(ciphertext, offset, len, blockSize, macSize);
+        int totalPad = checkPaddingConstantTime(ciphertext, offset, blocks_length, blockSize, encryptThenMAC ? 0 : macSize);
 
-        int macInputLen = len - totalPad - macSize;
+        int dec_output_length = blocks_length - totalPad;
 
-        byte[] decryptedMac = Arrays.copyOfRange(ciphertext, offset + macInputLen, offset + macInputLen + macSize);
-        byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen, len
-            - macSize, randomData);
-
-        boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, decryptedMac);
-
-        if (badMac || totalPad == 0)
+        if (!encryptThenMAC)
         {
-            throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+            dec_output_length -= macSize;
+            int macInputLen = dec_output_length;
+            int macOff = offset + macInputLen;
+            byte[] receivedMac = Arrays.copyOfRange(ciphertext, macOff, macOff + macSize);
+            byte[] calculatedMac = readMac.calculateMacConstantTime(seqNo, type, ciphertext, offset, macInputLen,
+                blocks_length - macSize, randomData);
+
+            boolean badMac = !Arrays.constantTimeAreEqual(calculatedMac, receivedMac);
+
+            if (badMac || totalPad == 0)
+            {
+                throw new TlsFatalAlert(AlertDescription.bad_record_mac);
+            }
         }
 
-        return Arrays.copyOfRange(ciphertext, offset, offset + macInputLen);
+        return Arrays.copyOfRange(ciphertext, offset, offset + dec_output_length);
     }
 
     protected int checkPaddingConstantTime(byte[] buf, int off, int len, int blockSize, int macSize)
@@ -251,7 +324,7 @@
         int dummyIndex = 0;
         byte padDiff = 0;
 
-        if ((context.getServerVersion().isSSL() && totalPad > blockSize) || (macSize + totalPad > len))
+        if ((TlsUtils.isSSL(context) && totalPad > blockSize) || (macSize + totalPad > len))
         {
             totalPad = 0;
         }
@@ -310,4 +383,4 @@
         }
         return n;
     }
-}
\ No newline at end of file
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
index 62444fa..7db86cd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
@@ -7,9 +7,18 @@
 public interface TlsClient
     extends TlsPeer
 {
-
     void init(TlsClientContext context);
 
+    /**
+     * Return the session this client wants to resume, if any. Note that the peer's certificate
+     * chain for the session (if any) may need to be periodically revalidated.
+     * 
+     * @return A {@link TlsSession} representing the resumable session to be used for this
+     *         connection, or null to use a new session.
+     * @see SessionParameters#getPeerCertificate()
+     */
+    TlsSession getSessionToResume();
+
     ProtocolVersion getClientHelloRecordLayerVersion();
 
     ProtocolVersion getClientVersion();
@@ -25,15 +34,18 @@
     void notifyServerVersion(ProtocolVersion selectedVersion)
         throws IOException;
 
+    /**
+     * Notifies the client of the session_id sent in the ServerHello.
+     * 
+     * @param sessionID
+     * @see {@link TlsContext#getResumableSession()}
+     */
     void notifySessionID(byte[] sessionID);
 
     void notifySelectedCipherSuite(int selectedCipherSuite);
 
     void notifySelectedCompressionMethod(short selectedCompressionMethod);
 
-    void notifySecureRenegotiation(boolean secureNegotiation)
-        throws IOException;
-
     // Hashtable is (Integer -> byte[])
     void processServerExtensions(Hashtable serverExtensions)
         throws IOException;
@@ -52,12 +64,6 @@
     Vector getClientSupplementalData()
         throws IOException;
 
-    TlsCompression getCompression()
-        throws IOException;
-
-    TlsCipher getCipher()
-        throws IOException;
-
     /**
      * RFC 5077 3.3. NewSessionTicket Handshake Message
      * <p/>
@@ -70,7 +76,4 @@
      */
     void notifyNewSessionTicket(NewSessionTicket newSessionTicket)
         throws IOException;
-
-    void notifyHandshakeComplete()
-        throws IOException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
index d91f7f8..b320144 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientContextImpl.java
@@ -6,7 +6,6 @@
     extends AbstractTlsContext
     implements TlsClientContext
 {
-
     TlsClientContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
     {
         super(secureRandom, securityParameters);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
index 33cd914..506560f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.crypto.tls;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -16,19 +15,15 @@
 public class TlsClientProtocol
     extends TlsProtocol
 {
-
     protected TlsClient tlsClient = null;
     protected TlsClientContextImpl tlsClientContext = null;
 
-    protected int[] offeredCipherSuites = null;
-    protected short[] offeredCompressionMethods = null;
-    protected Hashtable clientExtensions = null;
-
-    protected int selectedCipherSuite;
-    protected short selectedCompressionMethod;
+    protected byte[] selectedSessionID = null;
 
     protected TlsKeyExchange keyExchange = null;
     protected TlsAuthentication authentication = null;
+
+    protected CertificateStatus certificateStatus = null;
     protected CertificateRequest certificateRequest = null;
 
     private static SecureRandom createSecureRandom()
@@ -61,11 +56,10 @@
     /**
      * Initiates a TLS handshake in the role of client
      *
-     * @param tlsClient
+     * @param tlsClient The {@link TlsClient} to use for the handshake.
      * @throws IOException If handshake was not successful.
      */
-    public void connect(TlsClient tlsClient)
-        throws IOException
+    public void connect(TlsClient tlsClient) throws IOException
     {
         if (tlsClient == null)
         {
@@ -73,7 +67,7 @@
         }
         if (this.tlsClient != null)
         {
-            throw new IllegalStateException("connect can only be called once");
+            throw new IllegalStateException("'connect' can only be called once");
         }
 
         this.tlsClient = tlsClient;
@@ -86,12 +80,32 @@
         this.tlsClient.init(tlsClientContext);
         this.recordStream.init(tlsClientContext);
 
+        TlsSession sessionToResume = tlsClient.getSessionToResume();
+        if (sessionToResume != null)
+        {
+            SessionParameters sessionParameters = sessionToResume.exportSessionParameters();
+            if (sessionParameters != null)
+            {
+                this.tlsSession = sessionToResume;
+                this.sessionParameters = sessionParameters;
+            }
+        }
+
         sendClientHelloMessage();
         this.connection_state = CS_CLIENT_HELLO;
 
         completeHandshake();
+    }
 
-        this.tlsClient.notifyHandshakeComplete();
+    protected void cleanupHandshake()
+    {
+        super.cleanupHandshake();
+
+        this.selectedSessionID = null;
+        this.keyExchange = null;
+        this.authentication = null;
+        this.certificateStatus = null;
+        this.certificateRequest = null;
     }
 
     protected AbstractTlsContext getContext()
@@ -104,37 +118,28 @@
         return tlsClient;
     }
 
-    protected void handleChangeCipherSpecMessage()
-        throws IOException
-    {
-
-        switch (this.connection_state)
-        {
-        case CS_CLIENT_FINISHED:
-        {
-            if (this.expectSessionTicket)
-            {
-                /*
-                 * RFC 5077 3.3. This message MUST be sent if the server included a SessionTicket
-                 * extension in the ServerHello.
-                 */
-                this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
-            }
-            // NB: Fall through to next case label
-        }
-        case CS_SERVER_SESSION_TICKET:
-            this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC;
-            break;
-        default:
-            this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
-        }
-    }
-
     protected void handleHandshakeMessage(short type, byte[] data)
         throws IOException
     {
         ByteArrayInputStream buf = new ByteArrayInputStream(data);
 
+        if (this.resumedSession)
+        {
+            if (type != HandshakeType.finished || this.connection_state != CS_SERVER_HELLO)
+            {
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            }
+
+            processFinishedMessage(buf);
+            this.connection_state = CS_SERVER_FINISHED;
+
+            sendFinishedMessage();
+            this.connection_state = CS_CLIENT_FINISHED;
+            this.connection_state = CS_END;
+
+            return;
+        }
+
         switch (type)
         {
         case HandshakeType.certificate:
@@ -150,72 +155,143 @@
             {
                 // Parse the Certificate message and send to cipher suite
 
-                Certificate serverCertificate = Certificate.parse(buf);
+                this.peerCertificate = Certificate.parse(buf);
 
                 assertEmpty(buf);
 
-                this.keyExchange.processServerCertificate(serverCertificate);
+                // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+                if (this.peerCertificate == null || this.peerCertificate.isEmpty())
+                {
+                    this.allowCertificateStatus = false;
+                }
+
+                this.keyExchange.processServerCertificate(this.peerCertificate);
 
                 this.authentication = tlsClient.getAuthentication();
-                this.authentication.notifyServerCertificate(serverCertificate);
+                this.authentication.notifyServerCertificate(this.peerCertificate);
 
                 break;
             }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
 
             this.connection_state = CS_SERVER_CERTIFICATE;
             break;
         }
-        case HandshakeType.finished:
+        case HandshakeType.certificate_status:
+        {
             switch (this.connection_state)
             {
-            case CS_SERVER_CHANGE_CIPHER_SPEC:
-                processFinishedMessage(buf);
-                this.connection_state = CS_SERVER_FINISHED;
+            case CS_SERVER_CERTIFICATE:
+            {
+                if (!this.allowCertificateStatus)
+                {
+                    /*
+                     * RFC 3546 3.6. If a server returns a "CertificateStatus" message, then the
+                     * server MUST have included an extension of type "status_request" with empty
+                     * "extension_data" in the extended server hello..
+                     */
+                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
+                }
+
+                this.certificateStatus = CertificateStatus.parse(buf);
+
+                assertEmpty(buf);
+
+                // TODO[RFC 3546] Figure out how to provide this to the client/authentication.
+
+                this.connection_state = CS_CERTIFICATE_STATUS;
                 break;
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
+        }
+        case HandshakeType.finished:
+        {
+            switch (this.connection_state)
+            {
+            case CS_CLIENT_FINISHED:
+            {
+                processFinishedMessage(buf);
+                this.connection_state = CS_SERVER_FINISHED;
+                this.connection_state = CS_END;
+                break;
+            }
+            default:
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            }
+            break;
+        }
         case HandshakeType.server_hello:
+        {
             switch (this.connection_state)
             {
             case CS_CLIENT_HELLO:
+            {
                 receiveServerHelloMessage(buf);
                 this.connection_state = CS_SERVER_HELLO;
 
-                securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite);
-                securityParameters.compressionAlgorithm = this.selectedCompressionMethod;
+                if (this.securityParameters.maxFragmentLength >= 0)
+                {
+                    int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength);
+                    recordStream.setPlaintextLimit(plainTextLimit);
+                }
+
+                this.securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(),
+                    this.securityParameters.getCipherSuite());
 
                 /*
                  * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
                  * verify_data_length has a verify_data_length equal to 12. This includes all
                  * existing cipher suites.
                  */
-                securityParameters.verifyDataLength = 12;
+                this.securityParameters.verifyDataLength = 12;
 
-                recordStream.notifyHelloComplete();
+                this.recordStream.notifyHelloComplete();
+
+                if (this.resumedSession)
+                {
+                    this.securityParameters.masterSecret = Arrays.clone(this.sessionParameters.getMasterSecret());
+                    this.recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
+
+                    sendChangeCipherSpecMessage();
+                }
+                else
+                {
+                    invalidateSession();
+
+                    if (this.selectedSessionID.length > 0)
+                    {
+                        this.tlsSession = new TlsSessionImpl(this.selectedSessionID, null);
+                    }
+                }
 
                 break;
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
+        }
         case HandshakeType.supplemental_data:
         {
             switch (this.connection_state)
             {
             case CS_SERVER_HELLO:
+            {
                 handleSupplementalData(readSupplementalDataMessage(buf));
                 break;
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
         case HandshakeType.server_hello_done:
+        {
             switch (this.connection_state)
             {
             case CS_SERVER_HELLO:
@@ -225,7 +301,6 @@
             }
             case CS_SERVER_SUPPLEMENTAL_DATA:
             {
-
                 // There was no server certificate message; check it's OK
                 this.keyExchange.skipServerCredentials();
                 this.authentication = null;
@@ -233,19 +308,22 @@
                 // NB: Fall through to next case label
             }
             case CS_SERVER_CERTIFICATE:
-
+            case CS_CERTIFICATE_STATUS:
+            {
                 // There was no server key exchange message; check it's OK
                 this.keyExchange.skipServerKeyExchange();
 
                 // NB: Fall through to next case label
-
+            }
             case CS_SERVER_KEY_EXCHANGE:
             case CS_CERTIFICATE_REQUEST:
-
+            {
                 assertEmpty(buf);
 
                 this.connection_state = CS_SERVER_HELLO_DONE;
 
+                this.recordStream.getHandshakeHash().sealHashAlgorithms();
+
                 Vector clientSupplementalData = tlsClient.getClientSupplementalData();
                 if (clientSupplementalData != null)
                 {
@@ -289,40 +367,56 @@
                  * in our CipherSuite.
                  */
                 sendClientKeyExchangeMessage();
+                this.connection_state = CS_CLIENT_KEY_EXCHANGE;
 
                 establishMasterSecret(getContext(), keyExchange);
+                recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
 
-                /*
-                 * Initialize our cipher suite
-                 */
-                recordStream.setPendingConnectionState(tlsClient.getCompression(), tlsClient.getCipher());
-
-                this.connection_state = CS_CLIENT_KEY_EXCHANGE;
+                TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish();
 
                 if (clientCreds != null && clientCreds instanceof TlsSignerCredentials)
                 {
+                    TlsSignerCredentials signerCredentials = (TlsSignerCredentials)clientCreds;
+
                     /*
-                     * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm
-                     * prepended from TLS 1.2
+                     * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
                      */
-                    TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds;
-                    byte[] md5andsha1 = recordStream.getCurrentHash(null);
-                    byte[] clientCertificateSignature = signerCreds.generateCertificateSignature(md5andsha1);
-                    sendCertificateVerifyMessage(clientCertificateSignature);
+                    SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+                    byte[] hash;
+
+                    if (TlsUtils.isTLSv12(getContext()))
+                    {
+                        signatureAndHashAlgorithm = signerCredentials.getSignatureAndHashAlgorithm();
+                        if (signatureAndHashAlgorithm == null)
+                        {
+                            throw new TlsFatalAlert(AlertDescription.internal_error);
+                        }
+
+                        hash = prepareFinishHash.getFinalHash(signatureAndHashAlgorithm.getHash());
+                    }
+                    else
+                    {
+                        signatureAndHashAlgorithm = null;
+                        hash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
+                    }
+
+                    byte[] signature = signerCredentials.generateCertificateSignature(hash);
+                    DigitallySigned certificateVerify = new DigitallySigned(signatureAndHashAlgorithm, signature);
+                    sendCertificateVerifyMessage(certificateVerify);
 
                     this.connection_state = CS_CERTIFICATE_VERIFY;
                 }
 
                 sendChangeCipherSpecMessage();
-                this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC;
-
                 sendFinishedMessage();
                 this.connection_state = CS_CLIENT_FINISHED;
                 break;
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+                throw new TlsFatalAlert(AlertDescription.handshake_failure);
             }
             break;
+        }
         case HandshakeType.server_key_exchange:
         {
             switch (this.connection_state)
@@ -334,7 +428,6 @@
             }
             case CS_SERVER_SUPPLEMENTAL_DATA:
             {
-
                 // There was no server certificate message; check it's OK
                 this.keyExchange.skipServerCredentials();
                 this.authentication = null;
@@ -342,14 +435,15 @@
                 // NB: Fall through to next case label
             }
             case CS_SERVER_CERTIFICATE:
-
+            case CS_CERTIFICATE_STATUS:
+            {
                 this.keyExchange.processServerKeyExchange(buf);
 
                 assertEmpty(buf);
                 break;
-
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
 
             this.connection_state = CS_SERVER_KEY_EXCHANGE;
@@ -360,12 +454,13 @@
             switch (this.connection_state)
             {
             case CS_SERVER_CERTIFICATE:
-
+            case CS_CERTIFICATE_STATUS:
+            {
                 // There was no server key exchange message; check it's OK
                 this.keyExchange.skipServerKeyExchange();
 
                 // NB: Fall through to next case label
-
+            }
             case CS_SERVER_KEY_EXCHANGE:
             {
                 if (this.authentication == null)
@@ -374,19 +469,26 @@
                      * RFC 2246 7.4.4. It is a fatal handshake_failure alert for an anonymous server
                      * to request client identification.
                      */
-                    this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
+                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
                 }
 
-                this.certificateRequest = CertificateRequest.parse(buf);
+                this.certificateRequest = CertificateRequest.parse(getContext(), buf);
 
                 assertEmpty(buf);
 
                 this.keyExchange.validateCertificateRequest(this.certificateRequest);
 
+                /*
+                 * TODO Give the client a chance to immediately select the CertificateVerify hash
+                 * algorithm here to avoid tracking the other hash algorithms unnecessarily?
+                 */
+                TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(),
+                    this.certificateRequest.getSupportedSignatureAlgorithms());
+
                 break;
             }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
 
             this.connection_state = CS_CERTIFICATE_REQUEST;
@@ -397,23 +499,32 @@
             switch (this.connection_state)
             {
             case CS_CLIENT_FINISHED:
+            {
                 if (!this.expectSessionTicket)
                 {
                     /*
                      * RFC 5077 3.3. This message MUST NOT be sent if the server did not include a
                      * SessionTicket extension in the ServerHello.
                      */
-                    this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                 }
+
+                /*
+                 * RFC 5077 3.4. If the client receives a session ticket from the server, then it
+                 * discards any Session ID that was sent in the ServerHello.
+                 */
+                invalidateSession();
+
                 receiveNewSessionTicketMessage(buf);
                 this.connection_state = CS_SERVER_SESSION_TICKET;
                 break;
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
         }
         case HandshakeType.hello_request:
-
+        {
             assertEmpty(buf);
 
             /*
@@ -422,27 +533,34 @@
              * if it does not wish to renegotiate a session, or the client may, if it wishes,
              * respond with a no_renegotiation alert.
              */
-            if (this.connection_state == CS_SERVER_FINISHED)
+            if (this.connection_state == CS_END)
             {
+                /*
+                 * RFC 5746 4.5 SSLv3 clients that refuse renegotiation SHOULD use a fatal
+                 * handshake_failure alert.
+                 */
+                if (TlsUtils.isSSL(getContext()))
+                {
+                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
+                }
+
                 String message = "Renegotiation not supported";
                 raiseWarning(AlertDescription.no_renegotiation, message);
             }
             break;
+        }
+        case HandshakeType.client_hello:
         case HandshakeType.client_key_exchange:
         case HandshakeType.certificate_verify:
-        case HandshakeType.client_hello:
         case HandshakeType.hello_verify_request:
         default:
-            // We do not support this!
-            this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            break;
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
     }
 
     protected void handleSupplementalData(Vector serverSupplementalData)
         throws IOException
     {
-
         this.tlsClient.processServerSupplementalData(serverSupplementalData);
         this.connection_state = CS_SERVER_SUPPLEMENTAL_DATA;
 
@@ -453,7 +571,6 @@
     protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
         NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);
 
         TlsProtocol.assertEmpty(buf);
@@ -464,23 +581,22 @@
     protected void receiveServerHelloMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
         ProtocolVersion server_version = TlsUtils.readVersion(buf);
         if (server_version.isDTLS())
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
         // Check that this matches what the server is sending in the record layer
-        if (!server_version.equals(recordStream.getReadVersion()))
+        if (!server_version.equals(this.recordStream.getReadVersion()))
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
         ProtocolVersion client_version = getContext().getClientVersion();
         if (!server_version.isEqualOrEarlierVersionOf(client_version))
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
         this.recordStream.setWriteVersion(server_version);
@@ -490,38 +606,41 @@
         /*
          * Read the server random
          */
-        securityParameters.serverRandom = TlsUtils.readFully(32, buf);
+        this.securityParameters.serverRandom = TlsUtils.readFully(32, buf);
 
-        byte[] sessionID = TlsUtils.readOpaque8(buf);
-        if (sessionID.length > 32)
+        this.selectedSessionID = TlsUtils.readOpaque8(buf);
+        if (this.selectedSessionID.length > 32)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
-        this.tlsClient.notifySessionID(sessionID);
+        this.tlsClient.notifySessionID(this.selectedSessionID);
+
+        this.resumedSession = this.selectedSessionID.length > 0 && this.tlsSession != null
+            && Arrays.areEqual(this.selectedSessionID, this.tlsSession.getSessionID());
 
         /*
          * Find out which CipherSuite the server has chosen and check that it was one of the offered
          * ones.
          */
-        this.selectedCipherSuite = TlsUtils.readUint16(buf);
-        if (!arrayContains(offeredCipherSuites, this.selectedCipherSuite)
-            || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
-            || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+        int selectedCipherSuite = TlsUtils.readUint16(buf);
+        if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite)
+            || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+            || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
-        this.tlsClient.notifySelectedCipherSuite(this.selectedCipherSuite);
+        this.tlsClient.notifySelectedCipherSuite(selectedCipherSuite);
 
         /*
          * Find out which CompressionMethod the server has chosen and check that it was one of the
          * offered ones.
          */
         short selectedCompressionMethod = TlsUtils.readUint8(buf);
-        if (!arrayContains(offeredCompressionMethods, selectedCompressionMethod))
+        if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod))
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
         this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod);
@@ -534,15 +653,7 @@
          * possibility that the extended server hello message could "break" existing TLS 1.0
          * clients.
          */
-
-        /*
-         * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
-         * extensions appearing in the client hello, and send a server hello containing no
-         * extensions.
-         */
-
-        // Integer -> byte[]
-        Hashtable serverExtensions = readExtensions(buf);
+        this.serverExtensions = readExtensions(buf);
 
         /*
          * RFC 3546 2.2 Note that the extended server hello message is only sent in response to an
@@ -551,9 +662,9 @@
          * However, see RFC 5746 exception below. We always include the SCSV, so an Extended Server
          * Hello is always allowed.
          */
-        if (serverExtensions != null)
+        if (this.serverExtensions != null)
         {
-            Enumeration e = serverExtensions.keys();
+            Enumeration e = this.serverExtensions.keys();
             while (e.hasMoreElements())
             {
                 Integer extType = (Integer)e.nextElement();
@@ -565,107 +676,169 @@
                  * only allowed because the client is signaling its willingness to receive the
                  * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
                  */
-                if (!extType.equals(EXT_RenegotiationInfo)
-                    && (clientExtensions == null || clientExtensions.get(extType) == null))
+                if (extType.equals(EXT_RenegotiationInfo))
                 {
-                    /*
-                     * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless
-                     * the same extension type appeared in the corresponding ClientHello. If a
-                     * client receives an extension type in ServerHello that it did not request in
-                     * the associated ClientHello, it MUST abort the handshake with an
-                     * unsupported_extension fatal alert.
-                     */
-                    this.failWithError(AlertLevel.fatal, AlertDescription.unsupported_extension);
+                    continue;
                 }
-            }
 
-            /*
-             * RFC 5746 3.4. Client Behavior: Initial Handshake
-             */
-            {
                 /*
-                 * When a ServerHello is received, the client MUST check if it includes the
-                 * "renegotiation_info" extension:
+                 * RFC 3546 2.3. If [...] the older session is resumed, then the server MUST ignore
+                 * extensions appearing in the client hello, and send a server hello containing no
+                 * extensions[.]
                  */
-                byte[] renegExtValue = (byte[])serverExtensions.get(EXT_RenegotiationInfo);
-                if (renegExtValue != null)
+                if (this.resumedSession)
                 {
-                    /*
-                     * If the extension is present, set the secure_renegotiation flag to TRUE. The
-                     * client MUST then verify that the length of the "renegotiated_connection"
-                     * field is zero, and if it is not, MUST abort the handshake (by sending a fatal
-                     * handshake_failure alert).
-                     */
-                    this.secure_renegotiation = true;
+                    // TODO[compat-gnutls] GnuTLS test server sends server extensions e.g. ec_point_formats
+                    // TODO[compat-openssl] OpenSSL test server sends server extensions e.g. ec_point_formats
+                    // TODO[compat-polarssl] PolarSSL test server sends server extensions e.g. ec_point_formats
+//                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+                }
 
-                    if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
-                    {
-                        this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
-                    }
+                /*
+                 * RFC 5246 7.4.1.4 An extension type MUST NOT appear in the ServerHello unless the
+                 * same extension type appeared in the corresponding ClientHello. If a client
+                 * receives an extension type in ServerHello that it did not request in the
+                 * associated ClientHello, it MUST abort the handshake with an unsupported_extension
+                 * fatal alert.
+                 */
+                if (null == TlsUtils.getExtensionData(this.clientExtensions, extType))
+                {
+                    throw new TlsFatalAlert(AlertDescription.unsupported_extension);
                 }
             }
-
-            this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket);
         }
 
-        tlsClient.notifySecureRenegotiation(this.secure_renegotiation);
-
-        if (clientExtensions != null)
+        /*
+         * RFC 5746 3.4. Client Behavior: Initial Handshake
+         */
         {
-            tlsClient.processServerExtensions(serverExtensions);
+            /*
+             * When a ServerHello is received, the client MUST check if it includes the
+             * "renegotiation_info" extension:
+             */
+            byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo);
+            if (renegExtData != null)
+            {
+                /*
+                 * If the extension is present, set the secure_renegotiation flag to TRUE. The
+                 * client MUST then verify that the length of the "renegotiated_connection"
+                 * field is zero, and if it is not, MUST abort the handshake (by sending a fatal
+                 * handshake_failure alert).
+                 */
+                this.secure_renegotiation = true;
+
+                if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+                {
+                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
+                }
+            }
+        }
+
+        // TODO[compat-gnutls] GnuTLS test server fails to send renegotiation_info extension when resuming
+        this.tlsClient.notifySecureRenegotiation(this.secure_renegotiation);
+
+        Hashtable sessionClientExtensions = clientExtensions, sessionServerExtensions = serverExtensions;
+        if (this.resumedSession)
+        {
+            if (selectedCipherSuite != this.sessionParameters.getCipherSuite()
+                || selectedCompressionMethod != this.sessionParameters.getCompressionAlgorithm())
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+
+            sessionClientExtensions = null;
+            sessionServerExtensions = this.sessionParameters.readServerExtensions();
+        }
+
+        this.securityParameters.cipherSuite = selectedCipherSuite;
+        this.securityParameters.compressionAlgorithm = selectedCompressionMethod;
+
+        if (sessionServerExtensions != null)
+        {
+            this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(sessionClientExtensions,
+                sessionServerExtensions, AlertDescription.illegal_parameter);
+
+            this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(sessionServerExtensions);
+
+            /*
+             * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+             * a session resumption handshake.
+             */
+            this.allowCertificateStatus = !this.resumedSession
+                && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions,
+                    TlsExtensionsUtils.EXT_status_request, AlertDescription.illegal_parameter);
+
+            this.expectSessionTicket = !this.resumedSession
+                && TlsUtils.hasExpectedEmptyExtensionData(sessionServerExtensions, TlsProtocol.EXT_SessionTicket,
+                    AlertDescription.illegal_parameter);
+        }
+
+        if (sessionClientExtensions != null)
+        {
+            this.tlsClient.processServerExtensions(sessionServerExtensions);
         }
     }
 
-    protected void sendCertificateVerifyMessage(byte[] data)
+    protected void sendCertificateVerifyMessage(DigitallySigned certificateVerify)
         throws IOException
     {
-        /*
-         * Send signature of handshake messages so far to prove we are the owner of the cert See RFC
-         * 2246 sections 4.7, 7.4.3 and 7.4.8
-         */
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.certificate_verify, bos);
-        TlsUtils.writeUint24(data.length + 2, bos);
-        TlsUtils.writeOpaque16(data, bos);
-        byte[] message = bos.toByteArray();
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_verify);
 
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        certificateVerify.encode(message);
+
+        message.writeToRecordStream();
     }
 
     protected void sendClientHelloMessage()
         throws IOException
     {
-
-        recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion());
-
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.client_hello, buf);
-
-        // Reserve space for length
-        TlsUtils.writeUint24(0, buf);
+        this.recordStream.setWriteVersion(this.tlsClient.getClientHelloRecordLayerVersion());
 
         ProtocolVersion client_version = this.tlsClient.getClientVersion();
         if (client_version.isDTLS())
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
         getContext().setClientVersion(client_version);
-        TlsUtils.writeVersion(client_version, buf);
-
-        buf.write(securityParameters.clientRandom);
-
-        // Session id
-        TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
 
         /*
-         * Cipher suites
+         * TODO RFC 5077 3.4. When presenting a ticket, the client MAY generate and include a
+         * Session ID in the TLS ClientHello.
          */
+        byte[] session_id = TlsUtils.EMPTY_BYTES;
+        if (this.tlsSession != null)
+        {
+            session_id = this.tlsSession.getSessionID();
+            if (session_id == null || session_id.length > 32)
+            {
+                session_id = TlsUtils.EMPTY_BYTES;
+            }
+        }
+
         this.offeredCipherSuites = this.tlsClient.getCipherSuites();
 
-        // Integer -> byte[]
+        this.offeredCompressionMethods = this.tlsClient.getCompressionMethods();
+
+        if (session_id.length > 0 && this.sessionParameters != null)
+        {
+            if (!Arrays.contains(this.offeredCipherSuites, sessionParameters.getCipherSuite())
+                || !Arrays.contains(this.offeredCompressionMethods, sessionParameters.getCompressionAlgorithm()))
+            {
+                session_id = TlsUtils.EMPTY_BYTES;
+            }
+        }
+
         this.clientExtensions = this.tlsClient.getClientExtensions();
 
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.client_hello);
+
+        TlsUtils.writeVersion(client_version, message);
+
+        message.write(this.securityParameters.getClientRandom());
+
+        TlsUtils.writeOpaque8(session_id, message);
+
         // Cipher Suites (and SCSV)
         {
             /*
@@ -673,60 +846,39 @@
              * or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling cipher suite value in the
              * ClientHello. Including both is NOT RECOMMENDED.
              */
-            boolean noRenegExt = clientExtensions == null || clientExtensions.get(EXT_RenegotiationInfo) == null;
+            byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo);
+            boolean noRenegExt = (null == renegExtData);
 
-            int count = offeredCipherSuites.length;
-            if (noRenegExt)
+            boolean noSCSV = !Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
+
+            if (noRenegExt && noSCSV)
             {
-                // Note: 1 extra slot for TLS_EMPTY_RENEGOTIATION_INFO_SCSV
-                ++count;
+                // TODO Consider whether to default to a client extension instead
+//                this.clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.clientExtensions);
+//                this.clientExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
+                this.offeredCipherSuites = Arrays.append(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV);
             }
 
-            TlsUtils.writeUint16(2 * count, buf);
-            TlsUtils.writeUint16Array(offeredCipherSuites, buf);
-
-            if (noRenegExt)
-            {
-                TlsUtils.writeUint16(CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV, buf);
-            }
+            TlsUtils.writeUint16ArrayWithUint16Length(offeredCipherSuites, message);
         }
 
-        // Compression methods
-        this.offeredCompressionMethods = this.tlsClient.getCompressionMethods();
+        TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message);
 
-        TlsUtils.writeUint8((short)offeredCompressionMethods.length, buf);
-        TlsUtils.writeUint8Array(offeredCompressionMethods, buf);
-
-        // Extensions
         if (clientExtensions != null)
         {
-            writeExtensions(buf, clientExtensions);
+            writeExtensions(message, clientExtensions);
         }
 
-        byte[] message = buf.toByteArray();
-
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
-
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 
     protected void sendClientKeyExchangeMessage()
         throws IOException
     {
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.client_key_exchange);
 
-        TlsUtils.writeUint8(HandshakeType.client_key_exchange, bos);
+        this.keyExchange.generateClientKeyExchange(message);
 
-        // Reserve space for length
-        TlsUtils.writeUint24(0, bos);
-
-        this.keyExchange.generateClientKeyExchange(bos);
-        byte[] message = bos.toByteArray();
-
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
-
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
index dfb1052..04781ef 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsContext.java
@@ -4,7 +4,6 @@
 
 public interface TlsContext
 {
-
     SecureRandom getSecureRandom();
 
     SecurityParameters getSecurityParameters();
@@ -15,6 +14,16 @@
 
     ProtocolVersion getServerVersion();
 
+    /**
+     * Used to get the resumable session, if any, used by this connection. Only available after the
+     * handshake has successfully completed.
+     * 
+     * @return A {@link TlsSession} representing the resumable session used by this connection, or
+     *         null if no resumable session available.
+     * @see {@link TlsPeer#notifyHandshakeComplete()}
+     */
+    TlsSession getResumableSession();
+
     Object getUserObject();
 
     void setUserObject(Object userObject);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
index 5737659..0abaee6 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHEKeyExchange.java
@@ -1,24 +1,17 @@
 package org.bouncycastle.crypto.tls;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.math.BigInteger;
 import java.util.Vector;
 
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
-import org.bouncycastle.crypto.generators.DHKeyPairGenerator;
-import org.bouncycastle.crypto.io.SignerInputStream;
-import org.bouncycastle.crypto.params.DHKeyGenerationParameters;
 import org.bouncycastle.crypto.params.DHParameters;
-import org.bouncycastle.crypto.params.DHPublicKeyParameters;
+import org.bouncycastle.util.io.TeeInputStream;
 
 public class TlsDHEKeyExchange
     extends TlsDHKeyExchange
 {
-
     protected TlsSignerCredentials serverCredentials = null;
 
     public TlsDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
@@ -29,7 +22,6 @@
     public void processServerCredentials(TlsCredentials serverCredentials)
         throws IOException
     {
-
         if (!(serverCredentials instanceof TlsSignerCredentials))
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -43,40 +35,50 @@
     public byte[] generateServerKeyExchange()
         throws IOException
     {
-
         if (this.dhParameters == null)
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        DigestInputBuffer buf = new DigestInputBuffer();
 
-        DHKeyPairGenerator kpg = new DHKeyPairGenerator();
-        kpg.init(new DHKeyGenerationParameters(context.getSecureRandom(), this.dhParameters));
-        AsymmetricCipherKeyPair kp = kpg.generateKeyPair();
+        this.dhAgreeServerPrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
+            this.dhParameters, buf);
 
-        BigInteger Ys = ((DHPublicKeyParameters)kp.getPublic()).getY();
+        /*
+         * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+         */
+        SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+        Digest d;
 
-        TlsDHUtils.writeDHParameter(dhParameters.getP(), buf);
-        TlsDHUtils.writeDHParameter(dhParameters.getG(), buf);
-        TlsDHUtils.writeDHParameter(Ys, buf);
+        if (TlsUtils.isTLSv12(context))
+        {
+            signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm();
+            if (signatureAndHashAlgorithm == null)
+            {
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
 
-        byte[] digestInput = buf.toByteArray();
+            d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash());
+        }
+        else
+        {
+            signatureAndHashAlgorithm = null;
+            d = new CombinedHash();
+        }
 
-        Digest d = new CombinedHash();
         SecurityParameters securityParameters = context.getSecurityParameters();
         d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
         d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
-        d.update(digestInput, 0, digestInput.length);
+        buf.updateDigest(d);
 
         byte[] hash = new byte[d.getDigestSize()];
         d.doFinal(hash, 0);
 
-        byte[] sigBytes = serverCredentials.generateCertificateSignature(hash);
-        /*
-         * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended from TLS 1.2
-         */
-        TlsUtils.writeOpaque16(sigBytes, buf);
+        byte[] signature = serverCredentials.generateCertificateSignature(hash);
+
+        DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+        signed_params.encode(buf);
 
         return buf.toByteArray();
     }
@@ -84,28 +86,28 @@
     public void processServerKeyExchange(InputStream input)
         throws IOException
     {
-
         SecurityParameters securityParameters = context.getSecurityParameters();
 
-        Signer signer = initVerifyer(tlsSigner, securityParameters);
-        InputStream sigIn = new SignerInputStream(input, signer);
+        SignerInputBuffer buf = new SignerInputBuffer();
+        InputStream teeIn = new TeeInputStream(input, buf);
 
-        BigInteger p = TlsDHUtils.readDHParameter(sigIn);
-        BigInteger g = TlsDHUtils.readDHParameter(sigIn);
-        BigInteger Ys = TlsDHUtils.readDHParameter(sigIn);
+        ServerDHParams params = ServerDHParams.parse(teeIn);
 
-        byte[] sigBytes = TlsUtils.readOpaque16(input);
-        if (!signer.verifySignature(sigBytes))
+        DigitallySigned signed_params = DigitallySigned.parse(context, input);
+
+        Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
+        buf.updateSigner(signer);
+        if (!signer.verifySignature(signed_params.getSignature()))
         {
             throw new TlsFatalAlert(AlertDescription.decrypt_error);
         }
 
-        this.dhAgreeServerPublicKey = validateDHPublicKey(new DHPublicKeyParameters(Ys, new DHParameters(p, g)));
+        this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(params.getPublicKey());
     }
 
-    protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+    protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
     {
-        Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+        Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
         signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
         signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
         return signer;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
index 60e5105..7d79f6a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHKeyExchange.java
@@ -7,7 +7,6 @@
 
 import org.bouncycastle.asn1.x509.KeyUsage;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.DHParameters;
 import org.bouncycastle.crypto.params.DHPrivateKeyParameters;
@@ -20,7 +19,6 @@
 public class TlsDHKeyExchange
     extends AbstractTlsKeyExchange
 {
-
     protected static final BigInteger ONE = BigInteger.valueOf(1);
     protected static final BigInteger TWO = BigInteger.valueOf(2);
 
@@ -32,11 +30,11 @@
     protected TlsAgreementCredentials agreementCredentials;
     protected DHPrivateKeyParameters dhAgreeClientPrivateKey;
 
+    protected DHPrivateKeyParameters dhAgreeServerPrivateKey;
     protected DHPublicKeyParameters dhAgreeClientPublicKey;
 
     public TlsDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, DHParameters dhParameters)
     {
-
         super(keyExchange, supportedSignatureAlgorithms);
 
         switch (keyExchange)
@@ -77,7 +75,6 @@
     public void processServerCertificate(Certificate serverCertificate)
         throws IOException
     {
-
         if (serverCertificate.isEmpty())
         {
             throw new TlsFatalAlert(AlertDescription.bad_certificate);
@@ -99,7 +96,7 @@
         {
             try
             {
-                this.dhAgreeServerPublicKey = validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
+                this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey((DHPublicKeyParameters)this.serverPublicKey);
             }
             catch (ClassCastException e)
             {
@@ -196,27 +193,16 @@
             return agreementCredentials.generateAgreement(dhAgreeServerPublicKey);
         }
 
-        return calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
-    }
+        if (dhAgreeServerPrivateKey != null)
+        {
+            return TlsDHUtils.calculateDHBasicAgreement(dhAgreeClientPublicKey, dhAgreeServerPrivateKey);
+        }
 
-    protected boolean areCompatibleParameters(DHParameters a, DHParameters b)
-    {
-        return a.getP().equals(b.getP()) && a.getG().equals(b.getG());
-    }
+        if (dhAgreeClientPrivateKey != null)
+        {
+            return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+        }
 
-    protected byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey)
-    {
-        return TlsDHUtils.calculateDHBasicAgreement(publicKey, privateKey);
-    }
-
-    protected AsymmetricCipherKeyPair generateDHKeyPair(DHParameters dhParams)
-    {
-        return TlsDHUtils.generateDHKeyPair(context.getSecureRandom(), dhParams);
-    }
-
-    protected DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
-        throws IOException
-    {
-        return TlsDHUtils.validateDHPublicKey(key);
+        throw new TlsFatalAlert(AlertDescription.internal_error);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
index 014e40f..748c879 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDHUtils.java
@@ -17,14 +17,16 @@
 
 public class TlsDHUtils
 {
-
     static final BigInteger ONE = BigInteger.valueOf(1);
     static final BigInteger TWO = BigInteger.valueOf(2);
 
-    public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey,
-                                                   DHPrivateKeyParameters privateKey)
+    public static boolean areCompatibleParameters(DHParameters a, DHParameters b)
     {
+        return a.getP().equals(b.getP()) && a.getG().equals(b.getG());
+    }
 
+    public static byte[] calculateDHBasicAgreement(DHPublicKeyParameters publicKey, DHPrivateKeyParameters privateKey)
+    {
         DHBasicAgreement basicAgreement = new DHBasicAgreement();
         basicAgreement.init(privateKey);
         BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
@@ -36,32 +38,37 @@
         return BigIntegers.asUnsignedByteArray(agreementValue);
     }
 
-    public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random,
-                                                            DHParameters dhParams)
+    public static AsymmetricCipherKeyPair generateDHKeyPair(SecureRandom random, DHParameters dhParams)
     {
         DHBasicKeyPairGenerator dhGen = new DHBasicKeyPairGenerator();
         dhGen.init(new DHKeyGenerationParameters(random, dhParams));
         return dhGen.generateKeyPair();
     }
 
-    public static DHPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random,
-                                                                            DHParameters dhParams, OutputStream output)
-        throws IOException
+    public static DHPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, DHParameters dhParams,
+        OutputStream output) throws IOException
     {
+        AsymmetricCipherKeyPair kp = generateDHKeyPair(random, dhParams);
 
-        AsymmetricCipherKeyPair dhAgreeClientKeyPair = generateDHKeyPair(random, dhParams);
-        DHPrivateKeyParameters dhAgreeClientPrivateKey = (DHPrivateKeyParameters)dhAgreeClientKeyPair
-            .getPrivate();
+        DHPublicKeyParameters dh_public = (DHPublicKeyParameters) kp.getPublic();
+        writeDHParameter(dh_public.getY(), output);
 
-        BigInteger Yc = ((DHPublicKeyParameters)dhAgreeClientKeyPair.getPublic()).getY();
-        byte[] keData = BigIntegers.asUnsignedByteArray(Yc);
-        TlsUtils.writeOpaque16(keData, output);
-
-        return dhAgreeClientPrivateKey;
+        return (DHPrivateKeyParameters) kp.getPrivate();
     }
 
-    public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key)
-        throws IOException
+    public static DHPrivateKeyParameters generateEphemeralServerKeyExchange(SecureRandom random, DHParameters dhParams,
+        OutputStream output) throws IOException
+    {
+        AsymmetricCipherKeyPair kp = TlsDHUtils.generateDHKeyPair(random, dhParams);
+
+        DHPublicKeyParameters dhPublicKey = (DHPublicKeyParameters)kp.getPublic();
+        ServerDHParams params = new ServerDHParams(dhPublicKey);
+        params.encode(output);
+
+        return (DHPrivateKeyParameters)kp.getPrivate();
+    }
+
+    public static DHPublicKeyParameters validateDHPublicKey(DHPublicKeyParameters key) throws IOException
     {
         BigInteger Y = key.getY();
         DHParameters params = key.getParameters();
@@ -86,14 +93,12 @@
         return key;
     }
 
-    public static BigInteger readDHParameter(InputStream input)
-        throws IOException
+    public static BigInteger readDHParameter(InputStream input) throws IOException
     {
         return new BigInteger(1, TlsUtils.readOpaque16(input));
     }
 
-    public static void writeDHParameter(BigInteger x, OutputStream output)
-        throws IOException
+    public static void writeDHParameter(BigInteger x, OutputStream output) throws IOException
     {
         TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(x), output);
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
index b0e8957..4cb8004 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSASigner.java
@@ -6,7 +6,6 @@
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
 import org.bouncycastle.crypto.digests.NullDigest;
-import org.bouncycastle.crypto.digests.SHA1Digest;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.DSADigestSigner;
@@ -14,44 +13,73 @@
 public abstract class TlsDSASigner
     extends AbstractTlsSigner
 {
-
-    public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+    public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm,
+        AsymmetricKeyParameter privateKey, byte[] hash)
         throws CryptoException
     {
-
-        // Note: Only use the SHA1 part of the hash
-        Signer signer = makeSigner(new NullDigest(), true,
+        Signer signer = makeSigner(algorithm, true, true,
             new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
-        signer.update(md5AndSha1, 16, 20);
+        if (algorithm == null)
+        {
+            // Note: Only use the SHA1 part of the (MD5/SHA1) hash
+            signer.update(hash, 16, 20);
+        }
+        else
+        {
+            signer.update(hash, 0, hash.length);
+        }
         return signer.generateSignature();
     }
 
-    public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
+    public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+        AsymmetricKeyParameter publicKey, byte[] hash)
         throws CryptoException
     {
-
-        // Note: Only use the SHA1 part of the hash
-        Signer signer = makeSigner(new NullDigest(), false, publicKey);
-        signer.update(md5AndSha1, 16, 20);
+        Signer signer = makeSigner(algorithm, true, false, publicKey);
+        if (algorithm == null)
+        {
+            // Note: Only use the SHA1 part of the (MD5/SHA1) hash
+            signer.update(hash, 16, 20);
+        }
+        else
+        {
+            signer.update(hash, 0, hash.length);
+        }
         return signer.verifySignature(sigBytes);
     }
 
-    public Signer createSigner(AsymmetricKeyParameter privateKey)
+    public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
     {
-        return makeSigner(new SHA1Digest(), true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+        return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
     }
 
-    public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+    public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
     {
-        return makeSigner(new SHA1Digest(), false, publicKey);
+        return makeSigner(algorithm, false, false, publicKey);
     }
 
-    protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp)
+    protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning,
+        CipherParameters cp)
     {
+        if ((algorithm != null) != TlsUtils.isTLSv12(context))
+        {
+            throw new IllegalStateException();
+        }
+
+        if (algorithm != null
+            && (algorithm.getHash() != HashAlgorithm.sha1 || algorithm.getSignature() != getSignatureAlgorithm()))
+        {
+            throw new IllegalStateException();
+        }
+
+        Digest d = raw ? new NullDigest() : TlsUtils.createHash(HashAlgorithm.sha1);
+
         Signer s = new DSADigestSigner(createDSAImpl(), d);
         s.init(forSigning, cp);
         return s;
     }
 
+    protected abstract short getSignatureAlgorithm();
+
     protected abstract DSA createDSAImpl();
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
index e0eeca9..cb698bf 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsDSSSigner.java
@@ -8,7 +8,6 @@
 public class TlsDSSSigner
     extends TlsDSASigner
 {
-
     public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
     {
         return publicKey instanceof DSAPublicKeyParameters;
@@ -18,4 +17,9 @@
     {
         return new DSASigner();
     }
+
+    protected short getSignatureAlgorithm()
+    {
+        return SignatureAlgorithm.dsa;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
index a49f83f..3e7ef39 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECCUtils.java
@@ -1,7 +1,6 @@
 package org.bouncycastle.crypto.tls;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -9,7 +8,7 @@
 import java.security.SecureRandom;
 import java.util.Hashtable;
 
-import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.agreement.ECDHBasicAgreement;
@@ -19,91 +18,63 @@
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.math.ec.ECFieldElement;
 import org.bouncycastle.math.ec.ECPoint;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.BigIntegers;
 import org.bouncycastle.util.Integers;
 
 public class TlsECCUtils
 {
-
     public static final Integer EXT_elliptic_curves = Integers.valueOf(ExtensionType.elliptic_curves);
     public static final Integer EXT_ec_point_formats = Integers.valueOf(ExtensionType.ec_point_formats);
 
-    private static final String[] curveNames = new String[]{"sect163k1", "sect163r1", "sect163r2", "sect193r1",
+    private static final String[] curveNames = new String[] { "sect163k1", "sect163r1", "sect163r2", "sect193r1",
         "sect193r2", "sect233k1", "sect233r1", "sect239k1", "sect283k1", "sect283r1", "sect409k1", "sect409r1",
         "sect571k1", "sect571r1", "secp160k1", "secp160r1", "secp160r2", "secp192k1", "secp192r1", "secp224k1",
-        "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",};
+        "secp224r1", "secp256k1", "secp256r1", "secp384r1", "secp521r1",
+        "brainpoolP256r1", "brainpoolP384r1", "brainpoolP512r1"};
 
-    public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves)
-        throws IOException
+    public static void addSupportedEllipticCurvesExtension(Hashtable extensions, int[] namedCurves) throws IOException
     {
-
         extensions.put(EXT_elliptic_curves, createSupportedEllipticCurvesExtension(namedCurves));
     }
 
     public static void addSupportedPointFormatsExtension(Hashtable extensions, short[] ecPointFormats)
         throws IOException
     {
-
         extensions.put(EXT_ec_point_formats, createSupportedPointFormatsExtension(ecPointFormats));
     }
 
-    public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions)
-        throws IOException
+    public static int[] getSupportedEllipticCurvesExtension(Hashtable extensions) throws IOException
     {
-
-        if (extensions == null)
-        {
-            return null;
-        }
-        byte[] extensionValue = (byte[])extensions.get(EXT_elliptic_curves);
-        if (extensionValue == null)
-        {
-            return null;
-        }
-        return readSupportedEllipticCurvesExtension(extensionValue);
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_elliptic_curves);
+        return extensionData == null ? null : readSupportedEllipticCurvesExtension(extensionData);
     }
 
-    public static short[] getSupportedPointFormatsExtension(Hashtable extensions)
-        throws IOException
+    public static short[] getSupportedPointFormatsExtension(Hashtable extensions) throws IOException
     {
-
-        if (extensions == null)
-        {
-            return null;
-        }
-        byte[] extensionValue = (byte[])extensions.get(EXT_ec_point_formats);
-        if (extensionValue == null)
-        {
-            return null;
-        }
-        return readSupportedPointFormatsExtension(extensionValue);
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_ec_point_formats);
+        return extensionData == null ? null : readSupportedPointFormatsExtension(extensionData);
     }
 
-    public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves)
-        throws IOException
+    public static byte[] createSupportedEllipticCurvesExtension(int[] namedCurves) throws IOException
     {
-
         if (namedCurves == null || namedCurves.length < 1)
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint16(2 * namedCurves.length, buf);
-        TlsUtils.writeUint16Array(namedCurves, buf);
-        return buf.toByteArray();
+        return TlsUtils.encodeUint16ArrayWithUint16Length(namedCurves);
     }
 
-    public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats)
-        throws IOException
+    public static byte[] createSupportedPointFormatsExtension(short[] ecPointFormats) throws IOException
     {
-
         if (ecPointFormats == null)
         {
-            ecPointFormats = new short[]{ECPointFormat.uncompressed};
+            ecPointFormats = new short[] { ECPointFormat.uncompressed };
         }
-        else if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed))
+        else if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
         {
             /*
              * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
@@ -118,22 +89,17 @@
             ecPointFormats = tmp;
         }
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8((short)ecPointFormats.length, buf);
-        TlsUtils.writeUint8Array(ecPointFormats, buf);
-        return buf.toByteArray();
+        return TlsUtils.encodeUint8ArrayWithUint8Length(ecPointFormats);
     }
 
-    public static int[] readSupportedEllipticCurvesExtension(byte[] extensionValue)
-        throws IOException
+    public static int[] readSupportedEllipticCurvesExtension(byte[] extensionData) throws IOException
     {
-
-        if (extensionValue == null)
+        if (extensionData == null)
         {
-            throw new IllegalArgumentException("'extensionValue' cannot be null");
+            throw new IllegalArgumentException("'extensionData' cannot be null");
         }
 
-        ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
 
         int length = TlsUtils.readUint16(buf);
         if (length < 2 || (length & 1) != 0)
@@ -148,16 +114,14 @@
         return namedCurves;
     }
 
-    public static short[] readSupportedPointFormatsExtension(byte[] extensionValue)
-        throws IOException
+    public static short[] readSupportedPointFormatsExtension(byte[] extensionData) throws IOException
     {
-
-        if (extensionValue == null)
+        if (extensionData == null)
         {
-            throw new IllegalArgumentException("'extensionValue' cannot be null");
+            throw new IllegalArgumentException("'extensionData' cannot be null");
         }
 
-        ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
 
         short length = TlsUtils.readUint8(buf);
         if (length < 1)
@@ -169,7 +133,7 @@
 
         TlsProtocol.assertEmpty(buf);
 
-        if (!TlsProtocol.arrayContains(ecPointFormats, ECPointFormat.uncompressed))
+        if (!Arrays.contains(ecPointFormats, ECPointFormat.uncompressed))
         {
             /*
              * RFC 4492 5.1. If the Supported Point Formats Extension is indeed sent, it MUST
@@ -195,7 +159,7 @@
         }
 
         // Lazily created the first time a particular curve is accessed
-        X9ECParameters ecP = SECNamedCurves.getByName(curveName);
+        X9ECParameters ecP = ECNamedCurveTable.getByName(curveName);
 
         if (ecP == null)
         {
@@ -227,6 +191,9 @@
     {
         switch (cipherSuite)
         {
+        /*
+         * RFC 4492
+         */
         case CipherSuite.TLS_ECDH_ECDSA_WITH_NULL_SHA:
         case CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA:
         case CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA:
@@ -252,6 +219,10 @@
         case CipherSuite.TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA:
         case CipherSuite.TLS_ECDH_anon_WITH_AES_128_CBC_SHA:
         case CipherSuite.TLS_ECDH_anon_WITH_AES_256_CBC_SHA:
+
+        /*
+         * RFC 5289
+         */
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
@@ -268,7 +239,38 @@
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+
+        /*
+         * RFC 5489
+         */
+        case CipherSuite.TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA256:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_RC4_128_SHA:
+
+        /*
+         * draft-josefsson-salsa20-tls-02 
+         */
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_ESTREAM_SALSA20_UMAC96:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_SHA1:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_SALSA20_UMAC96:
+
             return true;
+
         default:
             return false;
         }
@@ -307,17 +309,13 @@
         return false;
     }
 
-    public static byte[] serializeECFieldElement(int fieldSize, BigInteger x)
-        throws IOException
+    public static byte[] serializeECFieldElement(int fieldSize, BigInteger x) throws IOException
     {
-        int requiredLength = (fieldSize + 7) / 8;
-        return BigIntegers.asUnsignedByteArray(requiredLength, x);
+        return BigIntegers.asUnsignedByteArray((fieldSize + 7) / 8, x);
     }
 
-    public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point)
-        throws IOException
+    public static byte[] serializeECPoint(short[] ecPointFormats, ECPoint point) throws IOException
     {
-
         ECCurve curve = point.getCurve();
 
         /*
@@ -341,12 +339,10 @@
     public static byte[] serializeECPublicKey(short[] ecPointFormats, ECPublicKeyParameters keyParameters)
         throws IOException
     {
-
         return serializeECPoint(ecPointFormats, keyParameters.getQ());
     }
 
-    public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding)
-        throws IOException
+    public static BigInteger deserializeECFieldElement(int fieldSize, byte[] encoding) throws IOException
     {
         int requiredLength = (fieldSize + 7) / 8;
         if (encoding.length != requiredLength)
@@ -356,22 +352,20 @@
         return new BigInteger(1, encoding);
     }
 
-    public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding)
-        throws IOException
+    public static ECPoint deserializeECPoint(short[] ecPointFormats, ECCurve curve, byte[] encoding) throws IOException
     {
         /*
          * NOTE: Here we implicitly decode compressed or uncompressed encodings. DefaultTlsClient by
          * default is set up to advertise that we can parse any encoding so this works fine, but
          * extra checks might be needed here if that were changed.
          */
+        // TODO Review handling of infinity and hybrid encodings
         return curve.decodePoint(encoding);
     }
 
     public static ECPublicKeyParameters deserializeECPublicKey(short[] ecPointFormats, ECDomainParameters curve_params,
-                                                               byte[] encoding)
-        throws IOException
+        byte[] encoding) throws IOException
     {
-
         try
         {
             ECPoint Y = deserializeECPoint(ecPointFormats, curve_params.getCurve(), encoding);
@@ -385,7 +379,6 @@
 
     public static byte[] calculateECDHBasicAgreement(ECPublicKeyParameters publicKey, ECPrivateKeyParameters privateKey)
     {
-
         ECDHBasicAgreement basicAgreement = new ECDHBasicAgreement();
         basicAgreement.init(privateKey);
         BigInteger agreementValue = basicAgreement.calculateAgreement(publicKey);
@@ -400,22 +393,29 @@
 
     public static AsymmetricCipherKeyPair generateECKeyPair(SecureRandom random, ECDomainParameters ecParams)
     {
-
         ECKeyPairGenerator keyPairGenerator = new ECKeyPairGenerator();
-        ECKeyGenerationParameters keyGenerationParameters = new ECKeyGenerationParameters(ecParams, random);
-        keyPairGenerator.init(keyGenerationParameters);
+        keyPairGenerator.init(new ECKeyGenerationParameters(ecParams, random));
         return keyPairGenerator.generateKeyPair();
     }
 
-    public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key)
-        throws IOException
+    public static ECPrivateKeyParameters generateEphemeralClientKeyExchange(SecureRandom random, short[] ecPointFormats,
+        ECDomainParameters ecParams, OutputStream output) throws IOException
+    {
+        AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(random, ecParams);
+
+        ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
+        writeECPoint(ecPointFormats, ecPublicKey.getQ(), output);
+
+        return (ECPrivateKeyParameters) kp.getPrivate();
+    }
+
+    public static ECPublicKeyParameters validateECPublicKey(ECPublicKeyParameters key) throws IOException
     {
         // TODO Check RFC 4492 for validation
         return key;
     }
 
-    public static int readECExponent(int fieldSize, InputStream input)
-        throws IOException
+    public static int readECExponent(int fieldSize, InputStream input) throws IOException
     {
         BigInteger K = readECParameter(input);
         if (K.bitLength() < 32)
@@ -429,14 +429,12 @@
         throw new TlsFatalAlert(AlertDescription.illegal_parameter);
     }
 
-    public static BigInteger readECFieldElement(int fieldSize, InputStream input)
-        throws IOException
+    public static BigInteger readECFieldElement(int fieldSize, InputStream input) throws IOException
     {
         return deserializeECFieldElement(fieldSize, TlsUtils.readOpaque8(input));
     }
 
-    public static BigInteger readECParameter(InputStream input)
-        throws IOException
+    public static BigInteger readECParameter(InputStream input) throws IOException
     {
         // TODO Are leading zeroes okay here?
         return new BigInteger(1, TlsUtils.readOpaque8(input));
@@ -445,7 +443,6 @@
     public static ECDomainParameters readECParameters(int[] namedCurves, short[] ecPointFormats, InputStream input)
         throws IOException
     {
-
         try
         {
             short curveType = TlsUtils.readUint8(input);
@@ -454,6 +451,8 @@
             {
             case ECCurveType.explicit_prime:
             {
+                checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_prime_curves);
+
                 BigInteger prime_p = readECParameter(input);
                 BigInteger a = readECFieldElement(prime_p.bitLength(), input);
                 BigInteger b = readECFieldElement(prime_p.bitLength(), input);
@@ -465,11 +464,12 @@
             }
             case ECCurveType.explicit_char2:
             {
+                checkNamedCurve(namedCurves, NamedCurve.arbitrary_explicit_char2_curves);
+
                 int m = TlsUtils.readUint16(input);
                 short basis = TlsUtils.readUint8(input);
                 ECCurve curve;
-                switch (basis)
-                {
+                switch (basis) {
                 case ECBasisType.ec_basis_trinomial:
                 {
                     int k = readECExponent(m, input);
@@ -509,15 +509,7 @@
                     throw new TlsFatalAlert(AlertDescription.illegal_parameter);
                 }
 
-                if (!TlsProtocol.arrayContains(namedCurves, namedCurve))
-                {
-                    /*
-                     * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
-                     * unless they can complete the handshake while respecting the choice of curves
-                     * and compression techniques specified by the client.
-                     */
-                    throw new TlsFatalAlert(AlertDescription.illegal_parameter);
-                }
+                checkNamedCurve(namedCurves, namedCurve);
 
                 return TlsECCUtils.getParametersForNamedCurve(namedCurve);
             }
@@ -531,47 +523,59 @@
         }
     }
 
-    public static void writeECExponent(int k, OutputStream output)
-        throws IOException
+    private static void checkNamedCurve(int[] namedCurves, int namedCurve) throws IOException
+    {
+        if (namedCurves != null && !Arrays.contains(namedCurves, namedCurve))
+        {
+            /*
+             * RFC 4492 4. [...] servers MUST NOT negotiate the use of an ECC cipher suite
+             * unless they can complete the handshake while respecting the choice of curves
+             * and compression techniques specified by the client.
+             */
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+    }
+
+    public static void writeECExponent(int k, OutputStream output) throws IOException
     {
         BigInteger K = BigInteger.valueOf(k);
         writeECParameter(K, output);
     }
 
-    public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output)
-        throws IOException
+    public static void writeECFieldElement(ECFieldElement x, OutputStream output) throws IOException
+    {
+        TlsUtils.writeOpaque8(x.getEncoded(), output);
+    }
+
+    public static void writeECFieldElement(int fieldSize, BigInteger x, OutputStream output) throws IOException
     {
         TlsUtils.writeOpaque8(serializeECFieldElement(fieldSize, x), output);
     }
 
-    public static void writeECParameter(BigInteger x, OutputStream output)
-        throws IOException
+    public static void writeECParameter(BigInteger x, OutputStream output) throws IOException
     {
         TlsUtils.writeOpaque8(BigIntegers.asUnsignedByteArray(x), output);
     }
 
     public static void writeExplicitECParameters(short[] ecPointFormats, ECDomainParameters ecParameters,
-                                                 OutputStream output)
-        throws IOException
+        OutputStream output) throws IOException
     {
-
         ECCurve curve = ecParameters.getCurve();
         if (curve instanceof ECCurve.Fp)
         {
-
             TlsUtils.writeUint8(ECCurveType.explicit_prime, output);
 
-            ECCurve.Fp fp = (ECCurve.Fp)curve;
+            ECCurve.Fp fp = (ECCurve.Fp) curve;
             writeECParameter(fp.getQ(), output);
-
         }
         else if (curve instanceof ECCurve.F2m)
         {
-
             TlsUtils.writeUint8(ECCurveType.explicit_char2, output);
 
-            ECCurve.F2m f2m = (ECCurve.F2m)curve;
-            TlsUtils.writeUint16(f2m.getM(), output);
+            ECCurve.F2m f2m = (ECCurve.F2m) curve;
+            int m = f2m.getM();
+            TlsUtils.checkUint16(m);
+            TlsUtils.writeUint16(m, output);
 
             if (f2m.isTrinomial())
             {
@@ -592,17 +596,20 @@
             throw new IllegalArgumentException("'ecParameters' not a known curve type");
         }
 
-        writeECFieldElement(curve.getFieldSize(), curve.getA().toBigInteger(), output);
-        writeECFieldElement(curve.getFieldSize(), curve.getB().toBigInteger(), output);
+        writeECFieldElement(curve.getA(), output);
+        writeECFieldElement(curve.getB(), output);
         TlsUtils.writeOpaque8(serializeECPoint(ecPointFormats, ecParameters.getG()), output);
         writeECParameter(ecParameters.getN(), output);
         writeECParameter(ecParameters.getH(), output);
     }
 
-    public static void writeNamedECParameters(int namedCurve, OutputStream output)
-        throws IOException
+    public static void writeECPoint(short[] ecPointFormats, ECPoint point, OutputStream output) throws IOException
     {
+        TlsUtils.writeOpaque8(TlsECCUtils.serializeECPoint(ecPointFormats, point), output);
+    }
 
+    public static void writeNamedECParameters(int namedCurve, OutputStream output) throws IOException
+    {
         if (!NamedCurve.refersToASpecificNamedCurve(namedCurve))
         {
             /*
@@ -614,6 +621,7 @@
         }
 
         TlsUtils.writeUint8(ECCurveType.named_curve, output);
+        TlsUtils.checkUint16(namedCurve);
         TlsUtils.writeUint16(namedCurve, output);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
index 1124560..d346ef4 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHEKeyExchange.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.crypto.tls;
 
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.Vector;
@@ -8,10 +7,11 @@
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
-import org.bouncycastle.crypto.io.SignerInputStream;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.io.TeeInputStream;
 
 /**
  * ECDHE key exchange (see RFC 4492)
@@ -19,11 +19,10 @@
 public class TlsECDHEKeyExchange
     extends TlsECDHKeyExchange
 {
-
     protected TlsSignerCredentials serverCredentials = null;
 
     public TlsECDHEKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves,
-                               short[] clientECPointFormats, short[] serverECPointFormats)
+        short[] clientECPointFormats, short[] serverECPointFormats)
     {
         super(keyExchange, supportedSignatureAlgorithms, namedCurves, clientECPointFormats, serverECPointFormats);
     }
@@ -31,7 +30,6 @@
     public void processServerCredentials(TlsCredentials serverCredentials)
         throws IOException
     {
-
         if (!(serverCredentials instanceof TlsSignerCredentials))
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -45,13 +43,13 @@
     public byte[] generateServerKeyExchange()
         throws IOException
     {
-
         /*
          * First we try to find a supported named curve from the client's list.
          */
         int namedCurve = -1;
         if (namedCurves == null)
         {
+            // TODO Let the peer choose the default named curve
             namedCurve = NamedCurve.secp256r1;
         }
         else
@@ -77,13 +75,13 @@
             /*
              * If no named curves are suitable, check if the client supports explicit curves.
              */
-            if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
+            if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_prime_curves))
             {
                 curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.secp256r1);
             }
-            else if (TlsProtocol.arrayContains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
+            else if (Arrays.contains(namedCurves, NamedCurve.arbitrary_explicit_char2_curves))
             {
-                curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect233r1);
+                curve_params = TlsECCUtils.getParametersForNamedCurve(NamedCurve.sect283r1);
             }
         }
 
@@ -97,12 +95,9 @@
         }
 
         AsymmetricCipherKeyPair kp = TlsECCUtils.generateECKeyPair(context.getSecureRandom(), curve_params);
-        this.ecAgreeServerPrivateKey = (ECPrivateKeyParameters)kp.getPrivate();
+        this.ecAgreePrivateKey = (ECPrivateKeyParameters)kp.getPrivate();
 
-        byte[] publicBytes = TlsECCUtils.serializeECPublicKey(clientECPointFormats,
-            (ECPublicKeyParameters)kp.getPublic());
-
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        DigestInputBuffer buf = new DigestInputBuffer();
 
         if (namedCurve < 0)
         {
@@ -113,25 +108,43 @@
             TlsECCUtils.writeNamedECParameters(namedCurve, buf);
         }
 
-        TlsUtils.writeOpaque8(publicBytes, buf);
+        ECPublicKeyParameters ecPublicKey = (ECPublicKeyParameters) kp.getPublic();
+        TlsECCUtils.writeECPoint(clientECPointFormats, ecPublicKey.getQ(), buf);
 
-        byte[] digestInput = buf.toByteArray();
+        /*
+         * RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2
+         */
+        SignatureAndHashAlgorithm signatureAndHashAlgorithm;
+        Digest d;
 
-        Digest d = new CombinedHash();
+        if (TlsUtils.isTLSv12(context))
+        {
+            signatureAndHashAlgorithm = serverCredentials.getSignatureAndHashAlgorithm();
+            if (signatureAndHashAlgorithm == null)
+            {
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            d = TlsUtils.createHash(signatureAndHashAlgorithm.getHash());
+        }
+        else
+        {
+            signatureAndHashAlgorithm = null;
+            d = new CombinedHash();
+        }
+
         SecurityParameters securityParameters = context.getSecurityParameters();
         d.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
         d.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
-        d.update(digestInput, 0, digestInput.length);
+        buf.updateDigest(d);
 
         byte[] hash = new byte[d.getDigestSize()];
         d.doFinal(hash, 0);
 
-        byte[] sigBytes = serverCredentials.generateCertificateSignature(hash);
-        /*
-         * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm prepended
-         * from TLS 1.2
-         */
-        TlsUtils.writeOpaque16(sigBytes, buf);
+        byte[] signature = serverCredentials.generateCertificateSignature(hash);
+
+        DigitallySigned signed_params = new DigitallySigned(signatureAndHashAlgorithm, signature);
+        signed_params.encode(buf);
 
         return buf.toByteArray();
     }
@@ -139,23 +152,25 @@
     public void processServerKeyExchange(InputStream input)
         throws IOException
     {
-
         SecurityParameters securityParameters = context.getSecurityParameters();
 
-        Signer signer = initVerifyer(tlsSigner, securityParameters);
-        InputStream sigIn = new SignerInputStream(input, signer);
+        SignerInputBuffer buf = new SignerInputBuffer();
+        InputStream teeIn = new TeeInputStream(input, buf);
 
-        ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, sigIn);
+        ECDomainParameters curve_params = TlsECCUtils.readECParameters(namedCurves, clientECPointFormats, teeIn);
 
-        byte[] point = TlsUtils.readOpaque8(sigIn);
+        byte[] point = TlsUtils.readOpaque8(teeIn);
 
-        byte[] sigByte = TlsUtils.readOpaque16(input);
-        if (!signer.verifySignature(sigByte))
+        DigitallySigned signed_params = DigitallySigned.parse(context, input);
+
+        Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
+        buf.updateSigner(signer);
+        if (!signer.verifySignature(signed_params.getSignature()))
         {
             throw new TlsFatalAlert(AlertDescription.decrypt_error);
         }
 
-        this.ecAgreeServerPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+        this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
             clientECPointFormats, curve_params, point));
     }
 
@@ -196,9 +211,9 @@
         }
     }
 
-    protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+    protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
     {
-        Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+        Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
         signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
         signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
         return signer;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
index 26c0975..9456352 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDHKeyExchange.java
@@ -7,7 +7,6 @@
 
 import org.bouncycastle.asn1.x509.KeyUsage;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
@@ -17,26 +16,21 @@
 /**
  * ECDH key exchange (see RFC 4492)
  */
-public class TlsECDHKeyExchange
-    extends AbstractTlsKeyExchange
+public class TlsECDHKeyExchange extends AbstractTlsKeyExchange
 {
-
     protected TlsSigner tlsSigner;
     protected int[] namedCurves;
     protected short[] clientECPointFormats, serverECPointFormats;
 
     protected AsymmetricKeyParameter serverPublicKey;
-    protected ECPublicKeyParameters ecAgreeServerPublicKey;
     protected TlsAgreementCredentials agreementCredentials;
-    protected ECPrivateKeyParameters ecAgreeClientPrivateKey;
 
-    protected ECPrivateKeyParameters ecAgreeServerPrivateKey;
-    protected ECPublicKeyParameters ecAgreeClientPublicKey;
+    protected ECPrivateKeyParameters ecAgreePrivateKey;
+    protected ECPublicKeyParameters ecAgreePublicKey;
 
     public TlsECDHKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, int[] namedCurves,
-                              short[] clientECPointFormats, short[] serverECPointFormats)
+        short[] clientECPointFormats, short[] serverECPointFormats)
     {
-
         super(keyExchange, supportedSignatureAlgorithms);
 
         switch (keyExchange)
@@ -71,16 +65,13 @@
         }
     }
 
-    public void skipServerCredentials()
-        throws IOException
+    public void skipServerCredentials() throws IOException
     {
         throw new TlsFatalAlert(AlertDescription.unexpected_message);
     }
 
-    public void processServerCertificate(Certificate serverCertificate)
-        throws IOException
+    public void processServerCertificate(Certificate serverCertificate) throws IOException
     {
-
         if (serverCertificate.isEmpty())
         {
             throw new TlsFatalAlert(AlertDescription.bad_certificate);
@@ -102,8 +93,7 @@
         {
             try
             {
-                this.ecAgreeServerPublicKey = TlsECCUtils
-                    .validateECPublicKey((ECPublicKeyParameters)this.serverPublicKey);
+                this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey((ECPublicKeyParameters) this.serverPublicKey);
             }
             catch (ClassCastException e)
             {
@@ -138,8 +128,7 @@
         }
     }
 
-    public void validateCertificateRequest(CertificateRequest certificateRequest)
-        throws IOException
+    public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
     {
         /*
          * RFC 4492 3. [...] The ECDSA_fixed_ECDH and RSA_fixed_ECDH mechanisms are usable with
@@ -164,14 +153,13 @@
         }
     }
 
-    public void processClientCredentials(TlsCredentials clientCredentials)
-        throws IOException
+    public void processClientCredentials(TlsCredentials clientCredentials) throws IOException
     {
         if (clientCredentials instanceof TlsAgreementCredentials)
         {
             // TODO Validate client cert has matching parameters (see 'TlsECCUtils.areOnSameCurve')?
 
-            this.agreementCredentials = (TlsAgreementCredentials)clientCredentials;
+            this.agreementCredentials = (TlsAgreementCredentials) clientCredentials;
         }
         else if (clientCredentials instanceof TlsSignerCredentials)
         {
@@ -183,37 +171,24 @@
         }
     }
 
-    public void generateClientKeyExchange(OutputStream output)
-        throws IOException
+    public void generateClientKeyExchange(OutputStream output) throws IOException
     {
-        if (agreementCredentials != null)
+        if (agreementCredentials == null)
         {
-            return;
+            this.ecAgreePrivateKey = TlsECCUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+                serverECPointFormats, ecAgreePublicKey.getParameters(), output);
         }
-
-        AsymmetricCipherKeyPair ecAgreeClientKeyPair = TlsECCUtils.generateECKeyPair(context.getSecureRandom(),
-            ecAgreeServerPublicKey.getParameters());
-        this.ecAgreeClientPrivateKey = (ECPrivateKeyParameters)ecAgreeClientKeyPair.getPrivate();
-
-        byte[] point = TlsECCUtils.serializeECPublicKey(serverECPointFormats,
-            (ECPublicKeyParameters)ecAgreeClientKeyPair.getPublic());
-
-        TlsUtils.writeOpaque8(point, output);
     }
 
-    public void processClientCertificate(Certificate clientCertificate)
-        throws IOException
+    public void processClientCertificate(Certificate clientCertificate) throws IOException
     {
-
         // TODO Extract the public key
         // TODO If the certificate is 'fixed', take the public key as ecAgreeClientPublicKey
     }
 
-    public void processClientKeyExchange(InputStream input)
-        throws IOException
+    public void processClientKeyExchange(InputStream input) throws IOException
     {
-
-        if (ecAgreeClientPublicKey != null)
+        if (ecAgreePublicKey != null)
         {
             // For ecdsa_fixed_ecdh and rsa_fixed_ecdh, the key arrived in the client certificate
             return;
@@ -221,28 +196,22 @@
 
         byte[] point = TlsUtils.readOpaque8(input);
 
-        ECDomainParameters curve_params = this.ecAgreeServerPrivateKey.getParameters();
+        ECDomainParameters curve_params = this.ecAgreePrivateKey.getParameters();
 
-        this.ecAgreeClientPublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
+        this.ecAgreePublicKey = TlsECCUtils.validateECPublicKey(TlsECCUtils.deserializeECPublicKey(
             serverECPointFormats, curve_params, point));
     }
 
-    public byte[] generatePremasterSecret()
-        throws IOException
+    public byte[] generatePremasterSecret() throws IOException
     {
         if (agreementCredentials != null)
         {
-            return agreementCredentials.generateAgreement(ecAgreeServerPublicKey);
+            return agreementCredentials.generateAgreement(ecAgreePublicKey);
         }
 
-        if (ecAgreeServerPrivateKey != null)
+        if (ecAgreePrivateKey != null)
         {
-            return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeClientPublicKey, ecAgreeServerPrivateKey);
-        }
-
-        if (ecAgreeClientPrivateKey != null)
-        {
-            return TlsECCUtils.calculateECDHBasicAgreement(ecAgreeServerPublicKey, ecAgreeClientPrivateKey);
+            return TlsECCUtils.calculateECDHBasicAgreement(ecAgreePublicKey, ecAgreePrivateKey);
         }
 
         throw new TlsFatalAlert(AlertDescription.internal_error);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
index 6809815..d7f8064 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsECDSASigner.java
@@ -8,7 +8,6 @@
 public class TlsECDSASigner
     extends TlsDSASigner
 {
-
     public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
     {
         return publicKey instanceof ECPublicKeyParameters;
@@ -18,4 +17,9 @@
     {
         return new ECDSASigner();
     }
+
+    protected short getSignatureAlgorithm()
+    {
+        return SignatureAlgorithm.ecdsa;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
index 2680136..eddf684 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsEncryptionCredentials.java
@@ -5,7 +5,6 @@
 public interface TlsEncryptionCredentials
     extends TlsCredentials
 {
-
     byte[] decryptPreMasterSecret(byte[] encryptedPreMasterSecret)
         throws IOException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java
new file mode 100644
index 0000000..fbc39dd
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsExtensionsUtils.java
@@ -0,0 +1,240 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Integers;
+
+public class TlsExtensionsUtils
+{
+    public static final Integer EXT_heartbeat = Integers.valueOf(ExtensionType.heartbeat);
+    public static final Integer EXT_max_fragment_length = Integers.valueOf(ExtensionType.max_fragment_length);
+    public static final Integer EXT_server_name = Integers.valueOf(ExtensionType.server_name);
+    public static final Integer EXT_status_request = Integers.valueOf(ExtensionType.status_request);
+    public static final Integer EXT_truncated_hmac = Integers.valueOf(ExtensionType.truncated_hmac);
+
+    public static Hashtable ensureExtensionsInitialised(Hashtable extensions)
+    {
+        return extensions == null ? new Hashtable() : extensions;
+    }
+
+    public static void addHeartbeatExtension(Hashtable extensions, HeartbeatExtension heartbeatExtension)
+        throws IOException
+    {
+        extensions.put(EXT_heartbeat, createHeartbeatExtension(heartbeatExtension));
+    }
+
+    public static void addMaxFragmentLengthExtension(Hashtable extensions, short maxFragmentLength)
+        throws IOException
+    {
+        extensions.put(EXT_max_fragment_length, createMaxFragmentLengthExtension(maxFragmentLength));
+    }
+
+    public static void addServerNameExtension(Hashtable extensions, ServerNameList serverNameList)
+        throws IOException
+    {
+        extensions.put(EXT_server_name, createServerNameExtension(serverNameList));
+    }
+
+    public static void addStatusRequestExtension(Hashtable extensions, CertificateStatusRequest statusRequest)
+        throws IOException
+    {
+        extensions.put(EXT_status_request, createStatusRequestExtension(statusRequest));
+    }
+
+    public static void addTruncatedHMacExtension(Hashtable extensions)
+    {
+        extensions.put(EXT_truncated_hmac, createTruncatedHMacExtension());
+    }
+
+    public static HeartbeatExtension getHeartbeatExtension(Hashtable extensions)
+        throws IOException
+    {
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_heartbeat);
+        return extensionData == null ? null : readHeartbeatExtension(extensionData);
+    }
+
+    public static short getMaxFragmentLengthExtension(Hashtable extensions)
+        throws IOException
+    {
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_max_fragment_length);
+        return extensionData == null ? -1 : readMaxFragmentLengthExtension(extensionData);
+    }
+
+    public static ServerNameList getServerNameExtension(Hashtable extensions)
+        throws IOException
+    {
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_server_name);
+        return extensionData == null ? null : readServerNameExtension(extensionData);
+    }
+
+    public static CertificateStatusRequest getStatusRequestExtension(Hashtable extensions)
+        throws IOException
+    {
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_status_request);
+        return extensionData == null ? null : readStatusRequestExtension(extensionData);
+    }
+
+    public static boolean hasTruncatedHMacExtension(Hashtable extensions) throws IOException
+    {
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_truncated_hmac);
+        return extensionData == null ? false : readTruncatedHMacExtension(extensionData);
+    }
+
+    public static byte[] createEmptyExtensionData()
+    {
+        return TlsUtils.EMPTY_BYTES;
+    }
+
+    public static byte[] createHeartbeatExtension(HeartbeatExtension heartbeatExtension)
+        throws IOException
+    {
+        if (heartbeatExtension == null)
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+        heartbeatExtension.encode(buf);
+
+        return buf.toByteArray();
+    }
+
+    public static byte[] createMaxFragmentLengthExtension(short maxFragmentLength)
+        throws IOException
+    {
+        if (!MaxFragmentLength.isValid(maxFragmentLength))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        return new byte[]{ (byte)maxFragmentLength };
+    }
+
+    public static byte[] createServerNameExtension(ServerNameList serverNameList)
+        throws IOException
+    {
+        if (serverNameList == null)
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+        
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+        
+        serverNameList.encode(buf);
+
+        return buf.toByteArray();
+    }
+
+    public static byte[] createStatusRequestExtension(CertificateStatusRequest statusRequest)
+        throws IOException
+    {
+        if (statusRequest == null)
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+        statusRequest.encode(buf);
+
+        return buf.toByteArray();
+    }
+
+    public static byte[] createTruncatedHMacExtension()
+    {
+        return createEmptyExtensionData();
+    }
+
+    public static HeartbeatExtension readHeartbeatExtension(byte[] extensionData)
+        throws IOException
+    {
+        if (extensionData == null)
+        {
+            throw new IllegalArgumentException("'extensionData' cannot be null");
+        }
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+        HeartbeatExtension heartbeatExtension = HeartbeatExtension.parse(buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        return heartbeatExtension;
+    }
+
+    public static short readMaxFragmentLengthExtension(byte[] extensionData)
+        throws IOException
+    {
+        if (extensionData == null)
+        {
+            throw new IllegalArgumentException("'extensionData' cannot be null");
+        }
+
+        if (extensionData.length != 1)
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+
+        short maxFragmentLength = (short)extensionData[0];
+
+        if (!MaxFragmentLength.isValid(maxFragmentLength))
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        return maxFragmentLength;
+    }
+
+    public static ServerNameList readServerNameExtension(byte[] extensionData)
+        throws IOException
+    {
+        if (extensionData == null)
+        {
+            throw new IllegalArgumentException("'extensionData' cannot be null");
+        }
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+        ServerNameList serverNameList = ServerNameList.parse(buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        return serverNameList;
+    }
+
+    public static CertificateStatusRequest readStatusRequestExtension(byte[] extensionData)
+        throws IOException
+    {
+        if (extensionData == null)
+        {
+            throw new IllegalArgumentException("'extensionData' cannot be null");
+        }
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+
+        CertificateStatusRequest statusRequest = CertificateStatusRequest.parse(buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        return statusRequest;
+    }
+
+    private static boolean readTruncatedHMacExtension(byte[] extensionData) throws IOException
+    {
+        if (extensionData == null)
+        {
+            throw new IllegalArgumentException("'extensionData' cannot be null");
+        }
+
+        if (extensionData.length != 0)
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        return true;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
index b17b8d7..1cb0f4d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java
@@ -5,10 +5,17 @@
 interface TlsHandshakeHash
     extends Digest
 {
-
     void init(TlsContext context);
 
-    TlsHandshakeHash commit();
+    TlsHandshakeHash notifyPRFDetermined();
 
-    TlsHandshakeHash fork();
+    void trackHashAlgorithm(short hashAlgorithm);
+
+    void sealHashAlgorithms();
+
+    TlsHandshakeHash stopTracking();
+
+    Digest forkPRFHash();
+
+    byte[] getFinalHash(short hashAlgorithm);
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
index ec11130..20dfef8 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsMac.java
@@ -1,8 +1,5 @@
 package org.bouncycastle.crypto.tls;
 
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.digests.LongDigest;
@@ -15,19 +12,19 @@
  */
 public class TlsMac
 {
-
     protected TlsContext context;
     protected byte[] secret;
     protected Mac mac;
     protected int digestBlockSize;
     protected int digestOverhead;
+    protected int macLength;
 
     /**
      * Generate a new instance of an TlsMac.
      *
      * @param context the TLS client context
      * @param digest  The digest to use.
-     * @param key     A byte-array where the key for this mac is located.
+     * @param key     A byte-array where the key for this MAC is located.
      * @param keyOff  The number of bytes to skip, before the key starts in the buffer.
      * @param len     The length of the key.
      */
@@ -51,7 +48,7 @@
             this.digestOverhead = 8;
         }
 
-        if (context.getServerVersion().isSSL())
+        if (TlsUtils.isSSL(context))
         {
             this.mac = new SSL3Mac(digest);
 
@@ -73,6 +70,12 @@
         }
 
         this.mac.init(keyParameter);
+
+        this.macLength = mac.getMacSize();
+        if (context.getSecurityParameters().truncatedHMac)
+        {
+            this.macLength = Math.min(this.macLength, 10);
+        }
     }
 
     /**
@@ -84,11 +87,11 @@
     }
 
     /**
-     * @return The Keysize of the mac.
+     * @return The output length of this MAC.
      */
     public int getSize()
     {
-        return mac.getMacSize();
+        return macLength;
     }
 
     /**
@@ -102,42 +105,38 @@
      */
     public byte[] calculateMac(long seqNo, short type, byte[] message, int offset, int length)
     {
+        /*
+         * TODO[draft-josefsson-salsa20-tls-02] 3. Moreover, in order to accommodate MAC algorithms
+         * like UMAC that require a nonce as part of their operation, the document extends the MAC
+         * algorithm as specified in the TLS protocol. The extended MAC includes a nonce as a second
+         * parameter. MAC algorithms that do not require a nonce, such as HMAC, are assumed to
+         * ignore the nonce input value. The MAC in a GenericStreamCipher is then calculated as
+         * follows.
+         */
 
         ProtocolVersion serverVersion = context.getServerVersion();
         boolean isSSL = serverVersion.isSSL();
 
-        ByteArrayOutputStream bosMac = new ByteArrayOutputStream(isSSL ? 11 : 13);
-        try
+        byte[] macHeader = new byte[isSSL ? 11 : 13];
+        TlsUtils.writeUint64(seqNo, macHeader, 0);
+        TlsUtils.writeUint8(type, macHeader, 8);
+        if (!isSSL)
         {
-            TlsUtils.writeUint64(seqNo, bosMac);
-            TlsUtils.writeUint8(type, bosMac);
-
-            if (!isSSL)
-            {
-                TlsUtils.writeVersion(serverVersion, bosMac);
-            }
-
-            TlsUtils.writeUint16(length, bosMac);
+            TlsUtils.writeVersion(serverVersion, macHeader, 9);
         }
-        catch (IOException e)
-        {
-            // This should never happen
-            throw new IllegalStateException("Internal error during mac calculation");
-        }
+        TlsUtils.writeUint16(length, macHeader, macHeader.length - 2);
 
-        byte[] macHeader = bosMac.toByteArray();
         mac.update(macHeader, 0, macHeader.length);
         mac.update(message, offset, length);
 
         byte[] result = new byte[mac.getMacSize()];
         mac.doFinal(result, 0);
-        return result;
+        return truncate(result);
     }
 
     public byte[] calculateMacConstantTime(long seqNo, short type, byte[] message, int offset, int length,
-                                           int fullLength, byte[] dummyData)
+        int fullLength, byte[] dummyData)
     {
-
         /*
          * Actual MAC only calculated on 'length' bytes...
          */
@@ -147,7 +146,7 @@
          * ...but ensure a constant number of complete digest blocks are processed (as many as would
          * be needed for 'fullLength' bytes of input).
          */
-        int headerLength = context.getServerVersion().isSSL() ? 11 : 13;
+        int headerLength = TlsUtils.isSSL(context) ? 11 : 13;
 
         // How many extra full blocks do we need to calculate?
         int extra = getDigestBlockCount(headerLength + fullLength) - getDigestBlockCount(headerLength + length);
@@ -164,9 +163,19 @@
         return result;
     }
 
-    private int getDigestBlockCount(int inputLength)
+    protected int getDigestBlockCount(int inputLength)
     {
         // NOTE: This calculation assumes a minimum of 1 pad byte
         return (inputLength + digestOverhead) / digestBlockSize;
     }
+
+    protected byte[] truncate(byte[] bs)
+    {
+        if (bs.length <= macLength)
+        {
+            return bs;
+        }
+
+        return Arrays.copyOf(bs, macLength);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
index d5b2b98..d1f6986 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsNullCipher.java
@@ -26,7 +26,6 @@
     public TlsNullCipher(TlsContext context, Digest clientWriteDigest, Digest serverWriteDigest)
         throws IOException
     {
-
         if ((clientWriteDigest == null) != (serverWriteDigest == null))
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -38,7 +37,6 @@
 
         if (clientWriteDigest != null)
         {
-
             int key_block_size = clientWriteDigest.getDigestSize()
                 + serverWriteDigest.getDigestSize();
             byte[] key_block = TlsUtils.calculateKeyBlock(context, key_block_size);
@@ -84,7 +82,6 @@
     public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
         throws IOException
     {
-
         if (writeMac == null)
         {
             return Arrays.copyOfRange(plaintext, offset, offset + len);
@@ -100,7 +97,6 @@
     public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
         throws IOException
     {
-
         if (readMac == null)
         {
             return Arrays.copyOfRange(ciphertext, offset, offset + len);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
index cfabb76..7217bac 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPSKKeyExchange.java
@@ -4,7 +4,6 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.math.BigInteger;
 import java.util.Vector;
 
 import org.bouncycastle.asn1.x509.KeyUsage;
@@ -22,33 +21,42 @@
 public class TlsPSKKeyExchange
     extends AbstractTlsKeyExchange
 {
-
     protected TlsPSKIdentity pskIdentity;
+    protected DHParameters dhParameters;
+    protected int[] namedCurves;
+    protected short[] clientECPointFormats, serverECPointFormats;
 
     protected byte[] psk_identity_hint = null;
 
-    protected DHPublicKeyParameters dhAgreeServerPublicKey = null;
-    protected DHPrivateKeyParameters dhAgreeClientPrivateKey = null;
+    protected DHPrivateKeyParameters dhAgreePrivateKey = null;
+    protected DHPublicKeyParameters dhAgreePublicKey = null;
 
     protected AsymmetricKeyParameter serverPublicKey = null;
     protected RSAKeyParameters rsaServerPublicKey = null;
+    protected TlsEncryptionCredentials serverCredentials = null;
     protected byte[] premasterSecret;
 
-    public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity)
+    public TlsPSKKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, TlsPSKIdentity pskIdentity,
+        DHParameters dhParameters, int[] namedCurves, short[] clientECPointFormats, short[] serverECPointFormats)
     {
         super(keyExchange, supportedSignatureAlgorithms);
 
         switch (keyExchange)
         {
+        case KeyExchangeAlgorithm.DHE_PSK:
+        case KeyExchangeAlgorithm.ECDHE_PSK:
         case KeyExchangeAlgorithm.PSK:
         case KeyExchangeAlgorithm.RSA_PSK:
-        case KeyExchangeAlgorithm.DHE_PSK:
             break;
         default:
             throw new IllegalArgumentException("unsupported key exchange algorithm");
         }
 
         this.pskIdentity = pskIdentity;
+        this.dhParameters = dhParameters;
+        this.namedCurves = namedCurves;
+        this.clientECPointFormats = clientECPointFormats;
+        this.serverECPointFormats = serverECPointFormats;
     }
 
     public void skipServerCredentials()
@@ -60,10 +68,61 @@
         }
     }
 
+    public void processServerCredentials(TlsCredentials serverCredentials)
+        throws IOException
+    {
+        if (!(serverCredentials instanceof TlsEncryptionCredentials))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        processServerCertificate(serverCredentials.getCertificate());
+
+        this.serverCredentials = (TlsEncryptionCredentials)serverCredentials;
+    }
+
+    public byte[] generateServerKeyExchange() throws IOException
+    {
+        // TODO[RFC 4279] Need a server-side PSK API to determine hint and resolve identities to keys
+        this.psk_identity_hint = null;
+
+        if (this.psk_identity_hint == null && !requiresServerKeyExchange())
+        {
+            return null;
+        }
+
+        ByteArrayOutputStream buf = new ByteArrayOutputStream();
+
+        if (this.psk_identity_hint == null)
+        {
+            TlsUtils.writeOpaque16(TlsUtils.EMPTY_BYTES, buf);
+        }
+        else
+        {
+            TlsUtils.writeOpaque16(this.psk_identity_hint, buf);
+        }
+
+        if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+        {
+            if (this.dhParameters == null)
+            {
+                throw new TlsFatalAlert(AlertDescription.internal_error);
+            }
+
+            this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralServerKeyExchange(context.getSecureRandom(),
+                this.dhParameters, buf);
+        }
+        else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+        {
+            // TODO[RFC 5489]
+        }
+
+        return buf.toByteArray();
+    }
+
     public void processServerCertificate(Certificate serverCertificate)
         throws IOException
     {
-
         if (keyExchange != KeyExchangeAlgorithm.RSA_PSK)
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
@@ -100,27 +159,30 @@
 
     public boolean requiresServerKeyExchange()
     {
-        return keyExchange == KeyExchangeAlgorithm.DHE_PSK;
+        switch (keyExchange)
+        {
+        case KeyExchangeAlgorithm.DHE_PSK:
+        case KeyExchangeAlgorithm.ECDHE_PSK:
+            return true;
+        default:
+            return false;
+        }
     }
 
     public void processServerKeyExchange(InputStream input)
         throws IOException
     {
-
         this.psk_identity_hint = TlsUtils.readOpaque16(input);
 
         if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
         {
-            byte[] pBytes = TlsUtils.readOpaque16(input);
-            byte[] gBytes = TlsUtils.readOpaque16(input);
-            byte[] YsBytes = TlsUtils.readOpaque16(input);
+            ServerDHParams serverDHParams = ServerDHParams.parse(input);
 
-            BigInteger p = new BigInteger(1, pBytes);
-            BigInteger g = new BigInteger(1, gBytes);
-            BigInteger Ys = new BigInteger(1, YsBytes);
-
-            this.dhAgreeServerPublicKey = TlsDHUtils.validateDHPublicKey(new DHPublicKeyParameters(Ys,
-                new DHParameters(p, g)));
+            this.dhAgreePublicKey = TlsDHUtils.validateDHPublicKey(serverDHParams.getPublicKey());
+        }
+        else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+        {
+            // TODO[RFC 5489]
         }
     }
 
@@ -139,7 +201,6 @@
     public void generateClientKeyExchange(OutputStream output)
         throws IOException
     {
-
         if (psk_identity_hint == null)
         {
             pskIdentity.skipIdentityHint();
@@ -153,22 +214,26 @@
 
         TlsUtils.writeOpaque16(psk_identity, output);
 
-        if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
+        if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
+        {
+            this.dhAgreePrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
+                dhAgreePublicKey.getParameters(), output);
+        }
+        else if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+        {
+            // TODO[RFC 5489]
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+        else if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
         {
             this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey,
                 output);
         }
-        else if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
-        {
-            this.dhAgreeClientPrivateKey = TlsDHUtils.generateEphemeralClientKeyExchange(context.getSecureRandom(),
-                dhAgreeServerPublicKey.getParameters(), output);
-        }
     }
 
     public byte[] generatePremasterSecret()
         throws IOException
     {
-
         byte[] psk = pskIdentity.getPSK();
         byte[] other_secret = generateOtherSecret(psk.length);
 
@@ -178,12 +243,22 @@
         return buf.toByteArray();
     }
 
-    protected byte[] generateOtherSecret(int pskLength)
+    protected byte[] generateOtherSecret(int pskLength) throws IOException
     {
-
         if (this.keyExchange == KeyExchangeAlgorithm.DHE_PSK)
         {
-            return TlsDHUtils.calculateDHBasicAgreement(dhAgreeServerPublicKey, dhAgreeClientPrivateKey);
+            if (dhAgreePrivateKey != null)
+            {
+                return TlsDHUtils.calculateDHBasicAgreement(dhAgreePublicKey, dhAgreePrivateKey);
+            }
+
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        if (this.keyExchange == KeyExchangeAlgorithm.ECDHE_PSK)
+        {
+            // TODO[RFC 5489]
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
         if (this.keyExchange == KeyExchangeAlgorithm.RSA_PSK)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
index e408002..88780ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsPeer.java
@@ -1,7 +1,14 @@
 package org.bouncycastle.crypto.tls;
 
+import java.io.IOException;
+
 public interface TlsPeer
 {
+    void notifySecureRenegotiation(boolean secureNegotiation) throws IOException;
+
+    TlsCompression getCompression() throws IOException;
+
+    TlsCipher getCipher() throws IOException;
 
     /**
      * This method will be called when an alert is raised by the protocol.
@@ -20,4 +27,9 @@
      * @param alertDescription {@link AlertDescription}
      */
     void notifyAlertReceived(short alertLevel, short alertDescription);
+
+    /**
+     * Notifies the peer that the handshake has been successfully completed.
+     */
+    void notifyHandshakeComplete() throws IOException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
index 6d8e3d3..2c3b094 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java
@@ -2,6 +2,7 @@
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -10,6 +11,7 @@
 import java.util.Hashtable;
 import java.util.Vector;
 
+import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Integers;
 
@@ -18,7 +20,6 @@
  */
 public abstract class TlsProtocol
 {
-
     protected static final Integer EXT_RenegotiationInfo = Integers.valueOf(ExtensionType.renegotiation_info);
     protected static final Integer EXT_SessionTicket = Integers.valueOf(ExtensionType.session_ticket);
 
@@ -32,25 +33,24 @@
     protected static final short CS_SERVER_HELLO = 2;
     protected static final short CS_SERVER_SUPPLEMENTAL_DATA = 3;
     protected static final short CS_SERVER_CERTIFICATE = 4;
-    protected static final short CS_SERVER_KEY_EXCHANGE = 5;
-    protected static final short CS_CERTIFICATE_REQUEST = 6;
-    protected static final short CS_SERVER_HELLO_DONE = 7;
-    protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 8;
-    protected static final short CS_CLIENT_CERTIFICATE = 9;
-    protected static final short CS_CLIENT_KEY_EXCHANGE = 10;
-    protected static final short CS_CERTIFICATE_VERIFY = 11;
-    protected static final short CS_CLIENT_CHANGE_CIPHER_SPEC = 12;
+    protected static final short CS_CERTIFICATE_STATUS = 5;
+    protected static final short CS_SERVER_KEY_EXCHANGE = 6;
+    protected static final short CS_CERTIFICATE_REQUEST = 7;
+    protected static final short CS_SERVER_HELLO_DONE = 8;
+    protected static final short CS_CLIENT_SUPPLEMENTAL_DATA = 9;
+    protected static final short CS_CLIENT_CERTIFICATE = 10;
+    protected static final short CS_CLIENT_KEY_EXCHANGE = 11;
+    protected static final short CS_CERTIFICATE_VERIFY = 12;
     protected static final short CS_CLIENT_FINISHED = 13;
     protected static final short CS_SERVER_SESSION_TICKET = 14;
-    protected static final short CS_SERVER_CHANGE_CIPHER_SPEC = 15;
-    protected static final short CS_SERVER_FINISHED = 16;
+    protected static final short CS_SERVER_FINISHED = 15;
+    protected static final short CS_END = 16;
 
     /*
      * Queues for data from some protocols.
      */
     private ByteQueue applicationDataQueue = new ByteQueue();
-    private ByteQueue changeCipherSpecQueue = new ByteQueue();
-    private ByteQueue alertQueue = new ByteQueue();
+    private ByteQueue alertQueue = new ByteQueue(2);
     private ByteQueue handshakeQueue = new ByteQueue();
 
     /*
@@ -65,13 +65,24 @@
     private volatile boolean closed = false;
     private volatile boolean failedWithError = false;
     private volatile boolean appDataReady = false;
-    private volatile boolean writeExtraEmptyRecords = true;
+    private volatile boolean splitApplicationDataRecords = true;
     private byte[] expected_verify_data = null;
 
+    protected TlsSession tlsSession = null;
+    protected SessionParameters sessionParameters = null;
     protected SecurityParameters securityParameters = null;
+    protected Certificate peerCertificate = null;
+
+    protected int[] offeredCipherSuites = null;
+    protected short[] offeredCompressionMethods = null;
+    protected Hashtable clientExtensions = null;
+    protected Hashtable serverExtensions = null;
 
     protected short connection_state = CS_START;
+    protected boolean resumedSession = false;
+    protected boolean receivedChangeCipherSpec = false;
     protected boolean secure_renegotiation = false;
+    protected boolean allowCertificateStatus = false;
     protected boolean expectSessionTicket = false;
 
     public TlsProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
@@ -84,8 +95,9 @@
 
     protected abstract TlsPeer getPeer();
 
-    protected abstract void handleChangeCipherSpecMessage()
-        throws IOException;
+    protected void handleChangeCipherSpecMessage() throws IOException
+    {
+    }
 
     protected abstract void handleHandshakeMessage(short type, byte[] buf)
         throws IOException;
@@ -93,37 +105,88 @@
     protected void handleWarningMessage(short description)
         throws IOException
     {
+    }
 
+    protected void cleanupHandshake()
+    {
+        if (this.expected_verify_data != null)
+        {
+            Arrays.fill(this.expected_verify_data, (byte)0);
+            this.expected_verify_data = null;
+        }
+
+        this.securityParameters.clear();
+        this.peerCertificate = null;
+
+        this.offeredCipherSuites = null;
+        this.offeredCompressionMethods = null;
+        this.clientExtensions = null;
+        this.serverExtensions = null;
+
+        this.resumedSession = false;
+        this.receivedChangeCipherSpec = false;
+        this.secure_renegotiation = false;
+        this.allowCertificateStatus = false;
+        this.expectSessionTicket = false;
     }
 
     protected void completeHandshake()
         throws IOException
     {
-
-        this.expected_verify_data = null;
-
-        /*
-         * We will now read data, until we have completed the handshake.
-         */
-        while (this.connection_state != CS_SERVER_FINISHED)
+        try
         {
-            safeReadRecord();
+            /*
+             * We will now read data, until we have completed the handshake.
+             */
+            while (this.connection_state != CS_END)
+            {
+                if (this.closed)
+                {
+                    // TODO What kind of exception/alert?
+                }
+
+                safeReadRecord();
+            }
+
+            this.recordStream.finaliseHandshake();
+
+            this.splitApplicationDataRecords = !TlsUtils.isTLSv11(getContext());
+
+            /*
+             * If this was an initial handshake, we are now ready to send and receive application data.
+             */
+            if (!appDataReady)
+            {
+                this.appDataReady = true;
+
+                this.tlsInputStream = new TlsInputStream(this);
+                this.tlsOutputStream = new TlsOutputStream(this);
+            }
+
+            if (this.tlsSession != null)
+            {
+                if (this.sessionParameters == null)
+                {
+                    this.sessionParameters = new SessionParameters.Builder()
+                        .setCipherSuite(this.securityParameters.cipherSuite)
+                        .setCompressionAlgorithm(this.securityParameters.compressionAlgorithm)
+                        .setMasterSecret(this.securityParameters.masterSecret)
+                        .setPeerCertificate(this.peerCertificate)
+                        // TODO Consider filtering extensions that aren't relevant to resumed sessions
+                        .setServerExtensions(this.serverExtensions)
+                        .build();
+
+                    this.tlsSession = new TlsSessionImpl(this.tlsSession.getSessionID(), this.sessionParameters);
+                }
+
+                getContext().setResumableSession(this.tlsSession);
+            }
+
+            getPeer().notifyHandshakeComplete();
         }
-
-        this.recordStream.finaliseHandshake();
-
-        ProtocolVersion version = getContext().getServerVersion();
-        this.writeExtraEmptyRecords = version.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10);
-
-        /*
-         * If this was an initial handshake, we are now ready to send and receive application data.
-         */
-        if (!appDataReady)
+        finally
         {
-            this.appDataReady = true;
-
-            this.tlsInputStream = new TlsInputStream(this);
-            this.tlsOutputStream = new TlsOutputStream(this);
+            cleanupHandshake();
         }
     }
 
@@ -135,26 +198,37 @@
          */
         switch (protocol)
         {
-        case ContentType.change_cipher_spec:
-            changeCipherSpecQueue.addData(buf, offset, len);
-            processChangeCipherSpec();
-            break;
         case ContentType.alert:
+        {
             alertQueue.addData(buf, offset, len);
             processAlert();
             break;
-        case ContentType.handshake:
-            handshakeQueue.addData(buf, offset, len);
-            processHandshake();
-            break;
+        }
         case ContentType.application_data:
+        {
             if (!appDataReady)
             {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             applicationDataQueue.addData(buf, offset, len);
             processApplicationData();
             break;
+        }
+        case ContentType.change_cipher_spec:
+        {
+            processChangeCipherSpec(buf, offset, len);
+            break;
+        }
+        case ContentType.handshake:
+        {
+            handshakeQueue.addData(buf, offset, len);
+            processHandshake();
+            break;
+        }
+        case ContentType.heartbeat:
+        {
+            // TODO[RFC 6520]
+        }
         default:
             /*
              * Uh, we don't know this protocol.
@@ -190,9 +264,7 @@
                     /*
                      * Read the message.
                      */
-                    byte[] buf = new byte[len];
-                    handshakeQueue.read(buf, 0, len, 4);
-                    handshakeQueue.removeData(len + 4);
+                    byte[] buf = handshakeQueue.removeData(len, 4);
 
                     /*
                      * RFC 2246 7.4.9. The value handshake_messages includes all handshake messages
@@ -205,7 +277,6 @@
                         break;
                     case HandshakeType.finished:
                     {
-
                         if (this.expected_verify_data == null)
                         {
                             this.expected_verify_data = createVerifyData(!getContext().isServer());
@@ -247,9 +318,7 @@
             /*
              * An alert is always 2 bytes. Read the alert.
              */
-            byte[] tmp = new byte[2];
-            alertQueue.read(tmp, 0, 2, 0);
-            alertQueue.removeData(2);
+            byte[] tmp = alertQueue.removeData(2, 0);
             short level = tmp[0];
             short description = tmp[1];
 
@@ -257,20 +326,17 @@
 
             if (level == AlertLevel.fatal)
             {
+                /*
+                 * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+                 * without proper close_notify messages with level equal to warning.
+                 */
+                invalidateSession();
 
                 this.failedWithError = true;
                 this.closed = true;
-                /*
-                 * Now try to close the stream, ignore errors.
-                 */
-                try
-                {
-                    recordStream.close();
-                }
-                catch (Exception e)
-                {
 
-                }
+                recordStream.safeClose();
+
                 throw new IOException(TLS_ERROR_MESSAGE);
             }
             else
@@ -300,27 +366,27 @@
      * @throws IOException If the message has an invalid content or the handshake is not in the correct
      * state.
      */
-    private void processChangeCipherSpec()
+    private void processChangeCipherSpec(byte[] buf, int off, int len)
         throws IOException
     {
-        while (changeCipherSpecQueue.size() > 0)
+        for (int i = 0; i < len; ++i)
         {
-            /*
-             * A change cipher spec message is only one byte with the value 1.
-             */
-            byte[] b = new byte[1];
-            changeCipherSpecQueue.read(b, 0, 1, 0);
-            changeCipherSpecQueue.removeData(1);
-            if (b[0] != 1)
+            short message = TlsUtils.readUint8(buf, off + i);
+
+            if (message != ChangeCipherSpec.change_cipher_spec)
             {
-                /*
-                 * This should never happen.
-                 */
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.decode_error);
             }
 
-            recordStream.receivedReadCipherSpec();
+            if (this.receivedChangeCipherSpec)
+            {
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
+            }
 
+            this.receivedChangeCipherSpec = true;
+
+            recordStream.receivedReadCipherSpec();
+    
             handleChangeCipherSpecMessage();
         }
     }
@@ -338,7 +404,6 @@
     protected int readApplicationData(byte[] buf, int offset, int len)
         throws IOException
     {
-
         if (len < 1)
         {
             return 0;
@@ -367,9 +432,9 @@
 
             safeReadRecord();
         }
+
         len = Math.min(len, applicationDataQueue.size());
-        applicationDataQueue.read(buf, offset, len, 0);
-        applicationDataQueue.removeData(len);
+        applicationDataQueue.removeData(buf, offset, len, 0);
         return len;
     }
 
@@ -378,13 +443,18 @@
     {
         try
         {
-            recordStream.readRecord();
+            if (!recordStream.readRecord())
+            {
+                // TODO It would be nicer to allow graceful connection close if between records
+//                this.failWithError(AlertLevel.warning, AlertDescription.close_notify);
+                throw new EOFException();
+            }
         }
         catch (TlsFatalAlert e)
         {
             if (!this.closed)
             {
-                this.failWithError(AlertLevel.fatal, e.getAlertDescription());
+                this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to read record", e);
             }
             throw e;
         }
@@ -392,7 +462,7 @@
         {
             if (!this.closed)
             {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
             }
             throw e;
         }
@@ -400,7 +470,7 @@
         {
             if (!this.closed)
             {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to read record", e);
             }
             throw e;
         }
@@ -417,7 +487,7 @@
         {
             if (!this.closed)
             {
-                this.failWithError(AlertLevel.fatal, e.getAlertDescription());
+                this.failWithError(AlertLevel.fatal, e.getAlertDescription(), "Failed to write record", e);
             }
             throw e;
         }
@@ -425,7 +495,7 @@
         {
             if (!closed)
             {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
             }
             throw e;
         }
@@ -433,7 +503,7 @@
         {
             if (!closed)
             {
-                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+                this.failWithError(AlertLevel.fatal, AlertDescription.internal_error, "Failed to write record", e);
             }
             throw e;
         }
@@ -467,25 +537,41 @@
             /*
              * RFC 5246 6.2.1. Zero-length fragments of Application data MAY be sent as they are
              * potentially useful as a traffic analysis countermeasure.
+             * 
+             * NOTE: Actually, implementations appear to have settled on 1/n-1 record splitting.
              */
-            if (this.writeExtraEmptyRecords)
+
+            if (this.splitApplicationDataRecords)
             {
                 /*
                  * Protect against known IV attack!
                  * 
-                 * DO NOT REMOVE THIS LINE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
+                 * DO NOT REMOVE THIS CODE, EXCEPT YOU KNOW EXACTLY WHAT YOU ARE DOING HERE.
                  */
-                safeWriteRecord(ContentType.application_data, TlsUtils.EMPTY_BYTES, 0, 0);
+                safeWriteRecord(ContentType.application_data, buf, offset, 1);
+                ++offset;
+                --len;
             }
 
-            /*
-             * We are only allowed to write fragments up to 2^14 bytes.
-             */
-            int toWrite = Math.min(len, 1 << 14);
+            if (len > 0)
+            {
+                // Fragment data according to the current fragment limit.
+                int toWrite = Math.min(len, recordStream.getPlaintextLimit());
+                safeWriteRecord(ContentType.application_data, buf, offset, toWrite);
+                offset += toWrite;
+                len -= toWrite;
+            }
+        }
+    }
 
-            safeWriteRecord(ContentType.application_data, buf, offset, toWrite);
-
-            offset += toWrite;
+    protected void writeHandshakeMessage(byte[] buf, int off, int len) throws IOException
+    {
+        while (len > 0)
+        {
+            // Fragment data according to the current fragment limit.
+            int toWrite = Math.min(len, recordStream.getPlaintextLimit());
+            safeWriteRecord(ContentType.handshake, buf, off, toWrite);
+            off += toWrite;
             len -= toWrite;
         }
     }
@@ -507,15 +593,16 @@
     }
 
     /**
-     * Terminate this connection with an alert.
-     * <p/>
-     * Can be used for normal closure too.
-     *
-     * @param alertLevel       The level of the alert, an be AlertLevel.fatal or AL_warning.
-     * @param alertDescription The exact alert message.
-     * @throws IOException If alert was fatal.
+     * Terminate this connection with an alert. Can be used for normal closure too.
+     * 
+     * @param alertLevel
+     *            See {@link AlertLevel} for values.
+     * @param alertDescription
+     *            See {@link AlertDescription} for values.
+     * @throws IOException
+     *             If alert was fatal.
      */
-    protected void failWithError(short alertLevel, short alertDescription)
+    protected void failWithError(short alertLevel, short alertDescription, String message, Exception cause)
         throws IOException
     {
         /*
@@ -531,27 +618,43 @@
             if (alertLevel == AlertLevel.fatal)
             {
                 /*
-                 * This is a fatal message.
+                 * RFC 2246 7.2.1. The session becomes unresumable if any connection is terminated
+                 * without proper close_notify messages with level equal to warning.
                  */
+                // TODO This isn't quite in the right place. Also, as of TLS 1.1 the above is obsolete.
+                invalidateSession();
+
                 this.failedWithError = true;
             }
-            raiseAlert(alertLevel, alertDescription, null, null);
-            recordStream.close();
-            if (alertLevel == AlertLevel.fatal)
+            raiseAlert(alertLevel, alertDescription, message, cause);
+            recordStream.safeClose();
+            if (alertLevel != AlertLevel.fatal)
             {
-                throw new IOException(TLS_ERROR_MESSAGE);
+                return;
             }
         }
-        else
+
+        throw new IOException(TLS_ERROR_MESSAGE);
+    }
+
+    protected void invalidateSession()
+    {
+        if (this.sessionParameters != null)
         {
-            throw new IOException(TLS_ERROR_MESSAGE);
+            this.sessionParameters.clear();
+            this.sessionParameters = null;
+        }
+
+        if (this.tlsSession != null)
+        {
+            this.tlsSession.invalidate();
+            this.tlsSession = null;
         }
     }
 
     protected void processFinishedMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
         byte[] verify_data = TlsUtils.readFully(expected_verify_data.length, buf);
 
         assertEmpty(buf);
@@ -564,14 +667,13 @@
             /*
              * Wrong checksum in the finished message.
              */
-            this.failWithError(AlertLevel.fatal, AlertDescription.decrypt_error);
+            throw new TlsFatalAlert(AlertDescription.decrypt_error);
         }
     }
 
     protected void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause)
         throws IOException
     {
-
         getPeer().notifyAlertRaised(alertLevel, alertDescription, message, cause);
 
         byte[] error = new byte[2];
@@ -590,7 +692,6 @@
     protected void sendCertificateMessage(Certificate certificate)
         throws IOException
     {
-
         if (certificate == null)
         {
             certificate = Certificate.EMPTY_CHAIN;
@@ -611,25 +712,17 @@
             }
         }
 
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.certificate, bos);
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate);
 
-        // Reserve space for length
-        TlsUtils.writeUint24(0, bos);
+        certificate.encode(message);
 
-        certificate.encode(bos);
-        byte[] message = bos.toByteArray();
-
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
-
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 
     protected void sendChangeCipherSpecMessage()
         throws IOException
     {
-        byte[] message = new byte[]{1};
+        byte[] message = new byte[]{ 1 };
         safeWriteRecord(ContentType.change_cipher_spec, message, 0, message.length);
         recordStream.sentWriteCipherSpec();
     }
@@ -639,33 +732,21 @@
     {
         byte[] verify_data = createVerifyData(getContext().isServer());
 
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.finished, bos);
-        TlsUtils.writeUint24(verify_data.length, bos);
-        bos.write(verify_data);
-        byte[] message = bos.toByteArray();
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.finished, verify_data.length);
 
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.write(verify_data);
+
+        message.writeToRecordStream();
     }
 
     protected void sendSupplementalDataMessage(Vector supplementalData)
         throws IOException
     {
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.supplemental_data);
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.supplemental_data, buf);
+        writeSupplementalData(message, supplementalData);
 
-        // Reserve space for length
-        TlsUtils.writeUint24(0, buf);
-
-        writeSupplementalData(buf, supplementalData);
-
-        byte[] message = buf.toByteArray();
-
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
-
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 
     protected byte[] createVerifyData(boolean isServer)
@@ -674,12 +755,12 @@
 
         if (isServer)
         {
-            return TlsUtils.calculateVerifyData(context, "server finished",
-                recordStream.getCurrentHash(TlsUtils.SSL_SERVER));
+            return TlsUtils.calculateVerifyData(context, ExporterLabel.server_finished,
+                getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_SERVER));
         }
 
-        return TlsUtils.calculateVerifyData(context, "client finished",
-            recordStream.getCurrentHash(TlsUtils.SSL_CLIENT));
+        return TlsUtils.calculateVerifyData(context, ExporterLabel.client_finished,
+            getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_CLIENT));
     }
 
     /**
@@ -702,7 +783,7 @@
             {
                 raiseWarning(AlertDescription.user_canceled, "User canceled handshake");
             }
-            this.failWithError(AlertLevel.warning, AlertDescription.close_notify);
+            this.failWithError(AlertLevel.warning, AlertDescription.close_notify, "Connection closed", null);
         }
     }
 
@@ -712,28 +793,18 @@
         recordStream.flush();
     }
 
-    protected static boolean arrayContains(short[] a, short n)
+    protected short processMaxFragmentLengthExtension(Hashtable clientExtensions, Hashtable serverExtensions, short alertDescription)
+        throws IOException
     {
-        for (int i = 0; i < a.length; ++i)
+        short maxFragmentLength = TlsExtensionsUtils.getMaxFragmentLengthExtension(serverExtensions);
+        if (maxFragmentLength >= 0 && !this.resumedSession)
         {
-            if (a[i] == n)
+            if (maxFragmentLength != TlsExtensionsUtils.getMaxFragmentLengthExtension(clientExtensions))
             {
-                return true;
+                throw new TlsFatalAlert(alertDescription);
             }
         }
-        return false;
-    }
-
-    protected static boolean arrayContains(int[] a, int n)
-    {
-        for (int i = 0; i < a.length; ++i)
-        {
-            if (a[i] == n)
-            {
-                return true;
-            }
-        }
-        return false;
+        return maxFragmentLength;
     }
 
     /**
@@ -753,25 +824,28 @@
 
     protected static byte[] createRandomBlock(SecureRandom random)
     {
+        random.setSeed(System.currentTimeMillis());
+
         byte[] result = new byte[32];
         random.nextBytes(result);
-        TlsUtils.writeGMTUnixTime(result, 0);
+        /*
+         * The consensus seems to be that using the time here is neither all that useful, nor
+         * secure. Perhaps there could be an option to (re-)enable it. Instead, we seed the random
+         * source with the current time to retain it's main benefit.
+         */
+//        TlsUtils.writeGMTUnixTime(result, 0);
         return result;
     }
 
     protected static byte[] createRenegotiationInfo(byte[] renegotiated_connection)
         throws IOException
     {
-
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeOpaque8(renegotiated_connection, buf);
-        return buf.toByteArray();
+        return TlsUtils.encodeOpaque8(renegotiated_connection);
     }
 
     protected static void establishMasterSecret(TlsContext context, TlsKeyExchange keyExchange)
         throws IOException
     {
-
         byte[] pre_master_secret = keyExchange.generatePremasterSecret();
 
         try
@@ -792,10 +866,26 @@
         }
     }
 
+    /**
+     * 'sender' only relevant to SSLv3
+     */
+    protected static byte[] getCurrentPRFHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender)
+    {
+        Digest d = handshakeHash.forkPRFHash();
+
+        if (sslSender != null && TlsUtils.isSSL(context))
+        {
+            d.update(sslSender, 0, sslSender.length);
+        }
+
+        byte[] bs = new byte[d.getDigestSize()];
+        d.doFinal(bs, 0);
+        return bs;
+    }
+
     protected static Hashtable readExtensions(ByteArrayInputStream input)
         throws IOException
     {
-
         if (input.available() < 1)
         {
             return null;
@@ -812,13 +902,13 @@
 
         while (buf.available() > 0)
         {
-            Integer extType = Integers.valueOf(TlsUtils.readUint16(buf));
-            byte[] extValue = TlsUtils.readOpaque16(buf);
+            Integer extension_type = Integers.valueOf(TlsUtils.readUint16(buf));
+            byte[] extension_data = TlsUtils.readOpaque16(buf);
 
             /*
              * RFC 3546 2.3 There MUST NOT be more than one extension of the same type.
              */
-            if (null != extensions.put(extType, extValue))
+            if (null != extensions.put(extension_type, extension_data))
             {
                 throw new TlsFatalAlert(AlertDescription.illegal_parameter);
             }
@@ -830,7 +920,6 @@
     protected static Vector readSupplementalDataMessage(ByteArrayInputStream input)
         throws IOException
     {
-
         byte[] supp_data = TlsUtils.readOpaque24(input);
 
         assertEmpty(input);
@@ -853,17 +942,18 @@
     protected static void writeExtensions(OutputStream output, Hashtable extensions)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
         Enumeration keys = extensions.keys();
         while (keys.hasMoreElements())
         {
-            Integer extType = (Integer)keys.nextElement();
-            byte[] extValue = (byte[])extensions.get(extType);
+            Integer key = (Integer)keys.nextElement();
+            int extension_type = key.intValue();
+            byte[] extension_data = (byte[])extensions.get(key);
 
-            TlsUtils.writeUint16(extType.intValue(), buf);
-            TlsUtils.writeOpaque16(extValue, buf);
+            TlsUtils.checkUint16(extension_type);
+            TlsUtils.writeUint16(extension_type, buf);
+            TlsUtils.writeOpaque16(extension_data, buf);
         }
 
         byte[] extBytes = buf.toByteArray();
@@ -874,14 +964,15 @@
     protected static void writeSupplementalData(OutputStream output, Vector supplementalData)
         throws IOException
     {
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
         for (int i = 0; i < supplementalData.size(); ++i)
         {
             SupplementalDataEntry entry = (SupplementalDataEntry)supplementalData.elementAt(i);
 
-            TlsUtils.writeUint16(entry.getDataType(), buf);
+            int supp_data_type = entry.getDataType();
+            TlsUtils.checkUint16(supp_data_type);
+            TlsUtils.writeUint16(supp_data_type, buf);
             TlsUtils.writeOpaque16(entry.getData(), buf);
         }
 
@@ -890,54 +981,137 @@
         TlsUtils.writeOpaque24(supp_data, output);
     }
 
-    protected static int getPRFAlgorithm(int ciphersuite)
+    protected static int getPRFAlgorithm(TlsContext context, int ciphersuite) throws IOException
     {
+        boolean isTLSv12 = TlsUtils.isTLSv12(context);
 
         switch (ciphersuite)
         {
         case CipherSuite.TLS_DH_DSS_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
-        case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
         case CipherSuite.TLS_DH_DSS_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
-        case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DH_DSS_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DH_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DH_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DH_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DHE_DSS_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_CCM:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_DHE_RSA_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_PSK_DHE_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_PSK_DHE_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CCM:
+        case CipherSuite.TLS_PSK_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_PSK_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CCM:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CCM_8:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_128_GCM_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_128_CCM_8:
+        case CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256:
         case CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA256:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM:
+        case CipherSuite.TLS_RSA_WITH_AES_256_CCM_8:
         case CipherSuite.TLS_RSA_WITH_NULL_SHA256:
-            return PRFAlgorithm.tls_prf_sha256;
+        {
+            if (isTLSv12)
+            {
+                return PRFAlgorithm.tls_prf_sha256;
+            }
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
 
-        case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
-        case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
-        case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
-        case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_DH_DSS_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DH_RSA_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_DSS_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_DHE_RSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384:
         case CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_PSK_WITH_AES_256_GCM_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_256_GCM_SHA384:
         case CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384:
-            return PRFAlgorithm.tls_prf_sha384;
+        {
+            if (isTLSv12)
+            {
+                return PRFAlgorithm.tls_prf_sha384;
+            }
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        case CipherSuite.TLS_DHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_DHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_ECDHE_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_PSK_WITH_NULL_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_AES_256_CBC_SHA384:
+        case CipherSuite.TLS_RSA_PSK_WITH_NULL_SHA384:
+        {
+            if (isTLSv12)
+            {
+                return PRFAlgorithm.tls_prf_sha384;
+            }
+            return PRFAlgorithm.tls_prf_legacy;
+        }
 
         default:
+        {
+            if (isTLSv12)
+            {
+                return PRFAlgorithm.tls_prf_sha256;
+            }
             return PRFAlgorithm.tls_prf_legacy;
         }
+        }
+    }
+
+    class HandshakeMessage extends ByteArrayOutputStream
+    {
+        HandshakeMessage(short handshakeType) throws IOException
+        {
+            this(handshakeType, 60);
+        }
+
+        HandshakeMessage(short handshakeType, int length) throws IOException
+        {
+            super(length + 4);
+            TlsUtils.writeUint8(handshakeType, this);
+            // Reserve space for length
+            count += 3;
+        }
+
+        void writeToRecordStream() throws IOException
+        {
+            // Patch actual length back in
+            int length = count - 4;
+            TlsUtils.checkUint24(length);
+            TlsUtils.writeUint24(length, buf, 1);
+            writeHandshakeMessage(buf, 0, count);
+            buf = null;
+        }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
index 24eec53..8970968 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAKeyExchange.java
@@ -40,7 +40,6 @@
     public void processServerCredentials(TlsCredentials serverCredentials)
         throws IOException
     {
-
         if (!(serverCredentials instanceof TlsEncryptionCredentials))
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
@@ -54,7 +53,6 @@
     public void processServerCertificate(Certificate serverCertificate)
         throws IOException
     {
-
         if (serverCertificate.isEmpty())
         {
             throw new TlsFatalAlert(AlertDescription.bad_certificate);
@@ -115,15 +113,14 @@
     public void generateClientKeyExchange(OutputStream output)
         throws IOException
     {
-        this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, this.rsaServerPublicKey, output);
+        this.premasterSecret = TlsRSAUtils.generateEncryptedPreMasterSecret(context, rsaServerPublicKey, output);
     }
 
     public void processClientKeyExchange(InputStream input)
         throws IOException
     {
-
         byte[] encryptedPreMasterSecret;
-        if (context.getServerVersion().isSSL())
+        if (TlsUtils.isSSL(context))
         {
             // TODO Do any SSLv3 clients actually include the length?
             encryptedPreMasterSecret = Streams.readAll(input);
@@ -133,68 +130,7 @@
             encryptedPreMasterSecret = TlsUtils.readOpaque16(input);
         }
 
-        ProtocolVersion clientVersion = context.getClientVersion();
-
-        /*
-         * RFC 5246 7.4.7.1.
-         */
-        {
-            // TODO Provide as configuration option?
-            boolean versionNumberCheckDisabled = false;
-
-            /*
-             * See notes regarding Bleichenbacher/Klima attack. The code here implements the first
-             * construction proposed there, which is RECOMMENDED.
-             */
-            byte[] R = new byte[48];
-            this.context.getSecureRandom().nextBytes(R);
-
-            byte[] M = TlsUtils.EMPTY_BYTES;
-            try
-            {
-                M = serverCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
-            }
-            catch (Exception e)
-            {
-                /*
-                 * In any case, a TLS server MUST NOT generate an alert if processing an
-                 * RSA-encrypted premaster secret message fails, or the version number is not as
-                 * expected. Instead, it MUST continue the handshake with a randomly generated
-                 * premaster secret.
-                 */
-            }
-
-            if (M.length != 48)
-            {
-                TlsUtils.writeVersion(clientVersion, R, 0);
-                this.premasterSecret = R;
-            }
-            else
-            {
-                /*
-                 * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
-                 * check the version number [..].
-                 */
-                if (versionNumberCheckDisabled && clientVersion.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
-                {
-                    /*
-                     * If the version number is TLS 1.0 or earlier, server implementations SHOULD
-                     * check the version number, but MAY have a configuration option to disable the
-                     * check.
-                     */
-                }
-                else
-                {
-                    /*
-                     * Note that explicitly constructing the pre_master_secret with the
-                     * ClientHello.client_version produces an invalid master_secret if the client
-                     * has sent the wrong version in the original pre_master_secret.
-                     */
-                    TlsUtils.writeVersion(clientVersion, M, 0);
-                }
-                this.premasterSecret = M;
-            }
-        }
+        this.premasterSecret = TlsRSAUtils.safeDecryptPreMasterSecret(context, serverCredentials, encryptedPreMasterSecret);
     }
 
     public byte[] generatePremasterSecret()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
index d9f7975..35538a7 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSASigner.java
@@ -5,6 +5,7 @@
 import org.bouncycastle.crypto.CryptoException;
 import org.bouncycastle.crypto.Digest;
 import org.bouncycastle.crypto.Signer;
+import org.bouncycastle.crypto.digests.NullDigest;
 import org.bouncycastle.crypto.encodings.PKCS1Encoding;
 import org.bouncycastle.crypto.engines.RSABlindedEngine;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
@@ -12,40 +13,37 @@
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.signers.GenericSigner;
 import org.bouncycastle.crypto.signers.RSADigestSigner;
-import org.bouncycastle.util.Arrays;
 
 public class TlsRSASigner
     extends AbstractTlsSigner
 {
-
-    public byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
+    public byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm,
+        AsymmetricKeyParameter privateKey, byte[] hash)
         throws CryptoException
     {
-
-        AsymmetricBlockCipher engine = createRSAImpl();
-        engine.init(true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
-        return engine.processBlock(md5AndSha1, 0, md5AndSha1.length);
-    }
-
-    public boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
-        throws CryptoException
-    {
-
-        AsymmetricBlockCipher engine = createRSAImpl();
-        engine.init(false, publicKey);
-        byte[] signed = engine.processBlock(sigBytes, 0, sigBytes.length);
-        return Arrays.constantTimeAreEqual(signed, md5AndSha1);
-    }
-
-    public Signer createSigner(AsymmetricKeyParameter privateKey)
-    {
-        return makeSigner(new CombinedHash(), true,
+        Signer signer = makeSigner(algorithm, true, true,
             new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+        signer.update(hash, 0, hash.length);
+        return signer.generateSignature();
     }
 
-    public Signer createVerifyer(AsymmetricKeyParameter publicKey)
+    public boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+        AsymmetricKeyParameter publicKey, byte[] hash)
+        throws CryptoException
     {
-        return makeSigner(new CombinedHash(), false, publicKey);
+        Signer signer = makeSigner(algorithm, true, false, publicKey);
+        signer.update(hash, 0, hash.length);
+        return signer.verifySignature(sigBytes);
+    }
+
+    public Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey)
+    {
+        return makeSigner(algorithm, false, true, new ParametersWithRandom(privateKey, this.context.getSecureRandom()));
+    }
+
+    public Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey)
+    {
+        return makeSigner(algorithm, false, false, publicKey);
     }
 
     public boolean isValidPublicKey(AsymmetricKeyParameter publicKey)
@@ -53,16 +51,41 @@
         return publicKey instanceof RSAKeyParameters && !publicKey.isPrivate();
     }
 
-    protected Signer makeSigner(Digest d, boolean forSigning, CipherParameters cp)
+    protected Signer makeSigner(SignatureAndHashAlgorithm algorithm, boolean raw, boolean forSigning,
+        CipherParameters cp)
     {
+        if ((algorithm != null) != TlsUtils.isTLSv12(context))
+        {
+            throw new IllegalStateException();
+        }
+
+        if (algorithm != null && algorithm.getSignature() != SignatureAlgorithm.rsa)
+        {
+            throw new IllegalStateException();
+        }
+
+        Digest d;
+        if (raw)
+        {
+            d = new NullDigest();
+        }
+        else if (algorithm == null)
+        {
+            d = new CombinedHash();
+        }
+        else
+        {
+            d = TlsUtils.createHash(algorithm.getHash());
+        }
+
         Signer s;
-        if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion()))
+        if (algorithm != null)
         {
             /*
              * RFC 5246 4.7. In RSA signing, the opaque vector contains the signature generated
              * using the RSASSA-PKCS1-v1_5 signature scheme defined in [PKCS1].
              */
-            s = new RSADigestSigner(d);
+            s = new RSADigestSigner(d, TlsUtils.getOIDForHashAlgorithm(algorithm.getHash()));
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
index f67e572..e3856bd 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsRSAUtils.java
@@ -12,8 +12,7 @@
 public class TlsRSAUtils
 {
     public static byte[] generateEncryptedPreMasterSecret(TlsContext context, RSAKeyParameters rsaServerPublicKey,
-                                                          OutputStream output)
-        throws IOException
+        OutputStream output) throws IOException
     {
         /*
          * Choose a PremasterSecret and send it encrypted to the server
@@ -29,7 +28,7 @@
         {
             byte[] encryptedPreMasterSecret = encoding.processBlock(premasterSecret, 0, premasterSecret.length);
 
-            if (context.getServerVersion().isSSL())
+            if (TlsUtils.isSSL(context))
             {
                 // TODO Do any SSLv3 servers actually expect the length?
                 output.write(encryptedPreMasterSecret);
@@ -49,4 +48,69 @@
 
         return premasterSecret;
     }
+
+    public static byte[] safeDecryptPreMasterSecret(TlsContext context, TlsEncryptionCredentials encryptionCredentials,
+        byte[] encryptedPreMasterSecret)
+    {
+        /*
+         * RFC 5246 7.4.7.1.
+         */
+
+        ProtocolVersion clientVersion = context.getClientVersion();
+
+        // TODO Provide as configuration option?
+        boolean versionNumberCheckDisabled = false;
+
+        /*
+         * See notes regarding Bleichenbacher/Klima attack. The code here implements the first
+         * construction proposed there, which is RECOMMENDED.
+         */
+        byte[] R = new byte[48];
+        context.getSecureRandom().nextBytes(R);
+
+        byte[] M = TlsUtils.EMPTY_BYTES;
+        try
+        {
+            M = encryptionCredentials.decryptPreMasterSecret(encryptedPreMasterSecret);
+        }
+        catch (Exception e)
+        {
+            /*
+             * In any case, a TLS server MUST NOT generate an alert if processing an
+             * RSA-encrypted premaster secret message fails, or the version number is not as
+             * expected. Instead, it MUST continue the handshake with a randomly generated
+             * premaster secret.
+             */
+        }
+
+        if (M.length != 48)
+        {
+            TlsUtils.writeVersion(clientVersion, R, 0);
+            return R;
+        }
+
+        /*
+         * If ClientHello.client_version is TLS 1.1 or higher, server implementations MUST
+         * check the version number [..].
+         */
+        if (versionNumberCheckDisabled && clientVersion.isEqualOrEarlierVersionOf(ProtocolVersion.TLSv10))
+        {
+            /*
+             * If the version number is TLS 1.0 or earlier, server implementations SHOULD
+             * check the version number, but MAY have a configuration option to disable the
+             * check.
+             */
+        }
+        else
+        {
+            /*
+             * Note that explicitly constructing the pre_master_secret with the
+             * ClientHello.client_version produces an invalid master_secret if the client
+             * has sent the wrong version in the original pre_master_secret.
+             */
+            TlsUtils.writeVersion(clientVersion, M, 0);
+        }
+
+        return M;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
index b928b91..452fbf9 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPKeyExchange.java
@@ -13,18 +13,16 @@
 import org.bouncycastle.crypto.agreement.srp.SRP6Client;
 import org.bouncycastle.crypto.agreement.srp.SRP6Util;
 import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.io.SignerInputStream;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.util.PublicKeyFactory;
 import org.bouncycastle.util.BigIntegers;
+import org.bouncycastle.util.io.TeeInputStream;
 
 /**
  * TLS 1.1 SRP key exchange (RFC 5054).
  */
-public class TlsSRPKeyExchange
-    extends AbstractTlsKeyExchange
+public class TlsSRPKeyExchange extends AbstractTlsKeyExchange
 {
-
     protected TlsSigner tlsSigner;
     protected byte[] identity;
     protected byte[] password;
@@ -37,7 +35,6 @@
 
     public TlsSRPKeyExchange(int keyExchange, Vector supportedSignatureAlgorithms, byte[] identity, byte[] password)
     {
-
         super(keyExchange, supportedSignatureAlgorithms);
 
         switch (keyExchange)
@@ -64,14 +61,12 @@
     {
         super.init(context);
 
-        if (this.tlsSigner != null)
-        {
+        if (this.tlsSigner != null) {
             this.tlsSigner.init(context);
         }
     }
 
-    public void skipServerCredentials()
-        throws IOException
+    public void skipServerCredentials() throws IOException
     {
         if (tlsSigner != null)
         {
@@ -79,10 +74,8 @@
         }
     }
 
-    public void processServerCertificate(Certificate serverCertificate)
-        throws IOException
+    public void processServerCertificate(Certificate serverCertificate) throws IOException
     {
-
         if (tlsSigner == null)
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
@@ -119,31 +112,31 @@
         return true;
     }
 
-    public void processServerKeyExchange(InputStream input)
-        throws IOException
+    public void processServerKeyExchange(InputStream input) throws IOException
     {
-
         SecurityParameters securityParameters = context.getSecurityParameters();
 
-        InputStream sigIn = input;
-        Signer signer = null;
+        SignerInputBuffer buf = null;
+        InputStream teeIn = input;
 
         if (tlsSigner != null)
         {
-            signer = initVerifyer(tlsSigner, securityParameters);
-            sigIn = new SignerInputStream(input, signer);
+            buf = new SignerInputBuffer();
+            teeIn = new TeeInputStream(input, buf);
         }
 
-        byte[] NBytes = TlsUtils.readOpaque16(sigIn);
-        byte[] gBytes = TlsUtils.readOpaque16(sigIn);
-        byte[] sBytes = TlsUtils.readOpaque8(sigIn);
-        byte[] BBytes = TlsUtils.readOpaque16(sigIn);
+        byte[] NBytes = TlsUtils.readOpaque16(teeIn);
+        byte[] gBytes = TlsUtils.readOpaque16(teeIn);
+        byte[] sBytes = TlsUtils.readOpaque8(teeIn);
+        byte[] BBytes = TlsUtils.readOpaque16(teeIn);
 
-        if (signer != null)
+        if (buf != null)
         {
-            byte[] sigByte = TlsUtils.readOpaque16(input);
+            DigitallySigned signed_params = DigitallySigned.parse(context, input);
 
-            if (!signer.verifySignature(sigByte))
+            Signer signer = initVerifyer(tlsSigner, signed_params.getAlgorithm(), securityParameters);
+            buf.updateSigner(signer);
+            if (!signer.verifySignature(signed_params.getSignature()))
             {
                 throw new TlsFatalAlert(AlertDescription.decrypt_error);
             }
@@ -153,7 +146,7 @@
         BigInteger g = new BigInteger(1, gBytes);
 
         // TODO Validate group parameters (see RFC 5054)
-        // handler.failWithError(AlertLevel.fatal, AlertDescription.insufficient_security);
+//        throw new TlsFatalAlert(AlertDescription.insufficient_security);
 
         this.s = sBytes;
 
@@ -173,28 +166,23 @@
         this.srpClient.init(N, g, new SHA1Digest(), context.getSecureRandom());
     }
 
-    public void validateCertificateRequest(CertificateRequest certificateRequest)
-        throws IOException
+    public void validateCertificateRequest(CertificateRequest certificateRequest) throws IOException
     {
         throw new TlsFatalAlert(AlertDescription.unexpected_message);
     }
 
-    public void processClientCredentials(TlsCredentials clientCredentials)
-        throws IOException
+    public void processClientCredentials(TlsCredentials clientCredentials) throws IOException
     {
         throw new TlsFatalAlert(AlertDescription.internal_error);
     }
 
-    public void generateClientKeyExchange(OutputStream output)
-        throws IOException
+    public void generateClientKeyExchange(OutputStream output) throws IOException
     {
-        byte[] keData = BigIntegers.asUnsignedByteArray(srpClient.generateClientCredentials(s, this.identity,
-            this.password));
-        TlsUtils.writeOpaque16(keData, output);
+        BigInteger A = srpClient.generateClientCredentials(s, this.identity, this.password);
+        TlsUtils.writeOpaque16(BigIntegers.asUnsignedByteArray(A), output);
     }
 
-    public byte[] generatePremasterSecret()
-        throws IOException
+    public byte[] generatePremasterSecret() throws IOException
     {
         try
         {
@@ -207,9 +195,9 @@
         }
     }
 
-    protected Signer initVerifyer(TlsSigner tlsSigner, SecurityParameters securityParameters)
+    protected Signer initVerifyer(TlsSigner tlsSigner, SignatureAndHashAlgorithm algorithm, SecurityParameters securityParameters)
     {
-        Signer signer = tlsSigner.createVerifyer(this.serverPublicKey);
+        Signer signer = tlsSigner.createVerifyer(algorithm, this.serverPublicKey);
         signer.update(securityParameters.clientRandom, 0, securityParameters.clientRandom.length);
         signer.update(securityParameters.serverRandom, 0, securityParameters.serverRandom.length);
         return signer;
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
new file mode 100644
index 0000000..7fe5fb8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRPUtils.java
@@ -0,0 +1,49 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.util.Hashtable;
+
+import org.bouncycastle.util.Integers;
+
+public class TlsSRPUtils
+{
+    public static final Integer EXT_SRP = Integers.valueOf(ExtensionType.srp);
+
+    public static void addSRPExtension(Hashtable extensions, byte[] identity) throws IOException
+    {
+        extensions.put(EXT_SRP, createSRPExtension(identity));
+    }
+
+    public static byte[] getSRPExtension(Hashtable extensions) throws IOException
+    {
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_SRP);
+        return extensionData == null ? null : readSRPExtension(extensionData);
+    }
+
+    public static byte[] createSRPExtension(byte[] identity) throws IOException
+    {
+        if (identity == null)
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+
+        return TlsUtils.encodeOpaque8(identity);
+    }
+
+    public static byte[] readSRPExtension(byte[] extensionData) throws IOException
+    {
+        if (extensionData == null)
+        {
+            throw new IllegalArgumentException("'extensionData' cannot be null");
+        }
+
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
+        byte[] identity = TlsUtils.readOpaque8(buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        return identity;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
index f82f94d..da98b7a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSRTPUtils.java
@@ -12,36 +12,24 @@
  */
 public class TlsSRTPUtils
 {
-
     public static final Integer EXT_use_srtp = Integers.valueOf(ExtensionType.use_srtp);
 
     public static void addUseSRTPExtension(Hashtable extensions, UseSRTPData useSRTPData)
         throws IOException
     {
-
         extensions.put(EXT_use_srtp, createUseSRTPExtension(useSRTPData));
     }
 
     public static UseSRTPData getUseSRTPExtension(Hashtable extensions)
         throws IOException
     {
-
-        if (extensions == null)
-        {
-            return null;
-        }
-        byte[] extensionValue = (byte[])extensions.get(EXT_use_srtp);
-        if (extensionValue == null)
-        {
-            return null;
-        }
-        return readUseSRTPExtension(extensionValue);
+        byte[] extensionData = TlsUtils.getExtensionData(extensions, EXT_use_srtp);
+        return extensionData == null ? null : readUseSRTPExtension(extensionData);
     }
 
     public static byte[] createUseSRTPExtension(UseSRTPData useSRTPData)
         throws IOException
     {
-
         if (useSRTPData == null)
         {
             throw new IllegalArgumentException("'useSRTPData' cannot be null");
@@ -50,9 +38,7 @@
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
         // SRTPProtectionProfiles
-        int[] protectionProfiles = useSRTPData.getProtectionProfiles();
-        TlsUtils.writeUint16(2 * protectionProfiles.length, buf);
-        TlsUtils.writeUint16Array(protectionProfiles, buf);
+        TlsUtils.writeUint16ArrayWithUint16Length(useSRTPData.getProtectionProfiles(), buf);
 
         // srtp_mki
         TlsUtils.writeOpaque8(useSRTPData.getMki(), buf);
@@ -60,16 +46,15 @@
         return buf.toByteArray();
     }
 
-    public static UseSRTPData readUseSRTPExtension(byte[] extensionValue)
+    public static UseSRTPData readUseSRTPExtension(byte[] extensionData)
         throws IOException
     {
-
-        if (extensionValue == null)
+        if (extensionData == null)
         {
-            throw new IllegalArgumentException("'extensionValue' cannot be null");
+            throw new IllegalArgumentException("'extensionData' cannot be null");
         }
 
-        ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
 
         // SRTPProtectionProfiles
         int length = TlsUtils.readUint16(buf);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
index 0b46391..85c0a9a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServer.java
@@ -7,11 +7,9 @@
 public interface TlsServer
     extends TlsPeer
 {
-
     void init(TlsServerContext context);
 
-    void notifyClientVersion(ProtocolVersion clientVersion)
-        throws IOException;
+    void notifyClientVersion(ProtocolVersion clientVersion) throws IOException;
 
     void notifyOfferedCipherSuites(int[] offeredCipherSuites)
         throws IOException;
@@ -19,9 +17,6 @@
     void notifyOfferedCompressionMethods(short[] offeredCompressionMethods)
         throws IOException;
 
-    void notifySecureRenegotiation(boolean secureNegotiation)
-        throws IOException;
-
     // Hashtable is (Integer -> byte[])
     void processClientExtensions(Hashtable clientExtensions)
         throws IOException;
@@ -46,32 +41,41 @@
     TlsCredentials getCredentials()
         throws IOException;
 
+    /**
+     * This method will be called (only) if the server included an extension of type
+     * "status_request" with empty "extension_data" in the extended server hello. See <i>RFC 3546
+     * 3.6. Certificate Status Request</i>. If a non-null {@link CertificateStatus} is returned, it
+     * is sent to the client as a handshake message of type "certificate_status".
+     * 
+     * @return A {@link CertificateStatus} to be sent to the client (or null for none).
+     * @throws IOException
+     */
+    CertificateStatus getCertificateStatus()
+        throws IOException;
+
     TlsKeyExchange getKeyExchange()
         throws IOException;
 
-    CertificateRequest getCertificateRequest();
+    CertificateRequest getCertificateRequest()
+        throws IOException;
 
     // Vector is (SupplementalDataEntry)
     void processClientSupplementalData(Vector clientSupplementalData)
         throws IOException;
 
     /**
-     * Called by the protocol handler to report the client certificate, only if a Certificate
-     * {@link #getCertificateRequest()} returned non-null. Note: this method is responsible for
-     * certificate verification and validation.
-     *
-     * @param clientCertificate the effective client certificate (may be an empty chain).
+     * Called by the protocol handler to report the client certificate, only if
+     * {@link #getCertificateRequest()} returned non-null.
+     * 
+     * Note: this method is responsible for certificate verification and validation.
+     * 
+     * @param clientCertificate
+     *            the effective client certificate (may be an empty chain).
      * @throws IOException
      */
     void notifyClientCertificate(Certificate clientCertificate)
         throws IOException;
 
-    TlsCompression getCompression()
-        throws IOException;
-
-    TlsCipher getCipher()
-        throws IOException;
-
     /**
      * RFC 5077 3.3. NewSessionTicket Handshake Message.
      * <p/>
@@ -83,7 +87,4 @@
      */
     NewSessionTicket getNewSessionTicket()
         throws IOException;
-
-    void notifyHandshakeComplete()
-        throws IOException;
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
index 2fa4029..48f028a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerContextImpl.java
@@ -6,7 +6,6 @@
     extends AbstractTlsContext
     implements TlsServerContext
 {
-
     TlsServerContextImpl(SecureRandom secureRandom, SecurityParameters securityParameters)
     {
         super(secureRandom, securityParameters);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
index 961669f..056d22a 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java
@@ -1,12 +1,10 @@
 package org.bouncycastle.crypto.tls;
 
 import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.security.SecureRandom;
-import java.util.Hashtable;
 import java.util.Vector;
 
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -17,25 +15,15 @@
 public class TlsServerProtocol
     extends TlsProtocol
 {
-
     protected TlsServer tlsServer = null;
     protected TlsServerContextImpl tlsServerContext = null;
 
-    protected int[] offeredCipherSuites;
-    protected short[] offeredCompressionMethods;
-    protected Hashtable clientExtensions;
-
-    protected int selectedCipherSuite;
-    protected short selectedCompressionMethod;
-    protected Hashtable serverExtensions;
-
     protected TlsKeyExchange keyExchange = null;
     protected TlsCredentials serverCredentials = null;
     protected CertificateRequest certificateRequest = null;
 
     protected short clientCertificateType = -1;
-    protected Certificate clientCertificate = null;
-    protected byte[] certificateVerifyHash = null;
+    protected TlsHandshakeHash prepareFinishHash = null;
 
     public TlsServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom)
     {
@@ -51,14 +39,13 @@
     public void accept(TlsServer tlsServer)
         throws IOException
     {
-
         if (tlsServer == null)
         {
             throw new IllegalArgumentException("'tlsServer' cannot be null");
         }
         if (this.tlsServer != null)
         {
-            throw new IllegalStateException("accept can only be called once");
+            throw new IllegalStateException("'accept' can only be called once");
         }
 
         this.tlsServer = tlsServer;
@@ -74,8 +61,16 @@
         this.recordStream.setRestrictReadVersion(false);
 
         completeHandshake();
+    }
 
-        this.tlsServer.notifyHandshakeComplete();
+    protected void cleanupHandshake()
+    {
+        super.cleanupHandshake();
+        
+        this.keyExchange = null;
+        this.serverCredentials = null;
+        this.certificateRequest = null;
+        this.prepareFinishHash = null;
     }
 
     protected AbstractTlsContext getContext()
@@ -88,36 +83,9 @@
         return tlsServer;
     }
 
-    protected void handleChangeCipherSpecMessage()
-        throws IOException
-    {
-
-        switch (this.connection_state)
-        {
-        case CS_CLIENT_KEY_EXCHANGE:
-        {
-            if (this.certificateVerifyHash != null)
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            }
-            // NB: Fall through to next case label
-        }
-        case CS_CERTIFICATE_VERIFY:
-        {
-            this.connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC;
-            break;
-        }
-        default:
-        {
-            this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
-        }
-        }
-    }
-
     protected void handleHandshakeMessage(short type, byte[] data)
         throws IOException
     {
-
         ByteArrayInputStream buf = new ByteArrayInputStream(data);
 
         switch (type)
@@ -134,21 +102,6 @@
                 sendServerHelloMessage();
                 this.connection_state = CS_SERVER_HELLO;
 
-                // TODO This block could really be done before actually sending the hello
-                {
-                    securityParameters.prfAlgorithm = getPRFAlgorithm(selectedCipherSuite);
-                    securityParameters.compressionAlgorithm = this.selectedCompressionMethod;
-
-                    /*
-                     * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify
-                     * verify_data_length has a verify_data_length equal to 12. This includes all
-                     * existing cipher suites.
-                     */
-                    securityParameters.verifyDataLength = 12;
-
-                    recordStream.notifyHelloComplete();
-                }
-
                 Vector serverSupplementalData = tlsServer.getServerSupplementalData();
                 if (serverSupplementalData != null)
                 {
@@ -160,6 +113,9 @@
                 this.keyExchange.init(getContext());
 
                 this.serverCredentials = tlsServer.getCredentials();
+
+                Certificate serverCertificate = null;
+
                 if (this.serverCredentials == null)
                 {
                     this.keyExchange.skipServerCredentials();
@@ -167,10 +123,29 @@
                 else
                 {
                     this.keyExchange.processServerCredentials(this.serverCredentials);
-                    sendCertificateMessage(this.serverCredentials.getCertificate());
+
+                    serverCertificate = this.serverCredentials.getCertificate();
+                    sendCertificateMessage(serverCertificate);
                 }
                 this.connection_state = CS_SERVER_CERTIFICATE;
 
+                // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes CertificateStatus
+                if (serverCertificate == null || serverCertificate.isEmpty())
+                {
+                    this.allowCertificateStatus = false;
+                }
+
+                if (this.allowCertificateStatus)
+                {
+                    CertificateStatus certificateStatus = tlsServer.getCertificateStatus();
+                    if (certificateStatus != null)
+                    {
+                        sendCertificateStatusMessage(certificateStatus);
+                    }
+                }
+
+                this.connection_state = CS_CERTIFICATE_STATUS;
+
                 byte[] serverKeyExchange = this.keyExchange.generateServerKeyExchange();
                 if (serverKeyExchange != null)
                 {
@@ -184,7 +159,11 @@
                     if (this.certificateRequest != null)
                     {
                         this.keyExchange.validateCertificateRequest(certificateRequest);
+
                         sendCertificateRequestMessage(certificateRequest);
+
+                        TlsUtils.trackHashAlgorithms(this.recordStream.getHandshakeHash(),
+                            this.certificateRequest.getSupportedSignatureAlgorithms());
                     }
                 }
                 this.connection_state = CS_CERTIFICATE_REQUEST;
@@ -192,12 +171,12 @@
                 sendServerHelloDoneMessage();
                 this.connection_state = CS_SERVER_HELLO_DONE;
 
+                this.recordStream.getHandshakeHash().sealHashAlgorithms();
+
                 break;
             }
             default:
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            }
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
@@ -212,9 +191,7 @@
                 break;
             }
             default:
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            }
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
@@ -231,16 +208,14 @@
             {
                 if (this.certificateRequest == null)
                 {
-                    this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                 }
                 receiveCertificateMessage(buf);
                 this.connection_state = CS_CLIENT_CERTIFICATE;
                 break;
             }
             default:
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            }
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
@@ -261,10 +236,7 @@
                 }
                 else
                 {
-
-                    ProtocolVersion equivalentTLSVersion = getContext().getServerVersion().getEquivalentTLSVersion();
-
-                    if (ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(equivalentTLSVersion))
+                    if (TlsUtils.isTLSv12(getContext()))
                     {
                         /*
                          * RFC 5246 If no suitable certificate is available, the client MUST send a
@@ -272,13 +244,13 @@
                          * 
                          * NOTE: In previous RFCs, this was SHOULD instead of MUST.
                          */
-                        this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                        throw new TlsFatalAlert(AlertDescription.unexpected_message);
                     }
-                    else if (equivalentTLSVersion.isSSL())
+                    else if (TlsUtils.isSSL(getContext()))
                     {
-                        if (clientCertificate == null)
+                        if (this.peerCertificate == null)
                         {
-                            this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                            throw new TlsFatalAlert(AlertDescription.unexpected_message);
                         }
                     }
                     else
@@ -295,9 +267,7 @@
                 break;
             }
             default:
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            }
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
@@ -312,18 +282,18 @@
                  * signing capability (i.e., all certificates except those containing fixed
                  * Diffie-Hellman parameters).
                  */
-                if (this.certificateVerifyHash == null)
+                if (!expectCertificateVerifyMessage())
                 {
-                    this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
                 }
+
                 receiveCertificateVerifyMessage(buf);
                 this.connection_state = CS_CERTIFICATE_VERIFY;
+
                 break;
             }
             default:
-            {
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            }
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
@@ -331,24 +301,33 @@
         {
             switch (this.connection_state)
             {
-            case CS_CLIENT_CHANGE_CIPHER_SPEC:
+            case CS_CLIENT_KEY_EXCHANGE:
+            {
+                if (expectCertificateVerifyMessage())
+                {
+                    throw new TlsFatalAlert(AlertDescription.unexpected_message);
+                }
+                // NB: Fall through to next case label
+            }
+            case CS_CERTIFICATE_VERIFY:
+            {
                 processFinishedMessage(buf);
                 this.connection_state = CS_CLIENT_FINISHED;
 
-                if (expectSessionTicket)
+                if (this.expectSessionTicket)
                 {
                     sendNewSessionTicketMessage(tlsServer.getNewSessionTicket());
+                    sendChangeCipherSpecMessage();
                 }
                 this.connection_state = CS_SERVER_SESSION_TICKET;
 
-                sendChangeCipherSpecMessage();
-                this.connection_state = CS_SERVER_CHANGE_CIPHER_SPEC;
-
                 sendFinishedMessage();
                 this.connection_state = CS_SERVER_FINISHED;
+                this.connection_state = CS_END;
                 break;
+            }
             default:
-                this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
+                throw new TlsFatalAlert(AlertDescription.unexpected_message);
             }
             break;
         }
@@ -360,9 +339,7 @@
         case HandshakeType.server_hello_done:
         case HandshakeType.session_ticket:
         default:
-            // We do not support this!
-            this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message);
-            break;
+            throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
     }
 
@@ -377,7 +354,7 @@
              * SSL 3.0 If the server has sent a certificate request Message, the client must send
              * either the certificate message or a no_certificate alert.
              */
-            if (getContext().getServerVersion().isSSL() && certificateRequest != null)
+            if (TlsUtils.isSSL(getContext()) && certificateRequest != null)
             {
                 notifyClientCertificate(Certificate.EMPTY_CHAIN);
             }
@@ -393,18 +370,17 @@
     protected void notifyClientCertificate(Certificate clientCertificate)
         throws IOException
     {
-
         if (certificateRequest == null)
         {
             throw new IllegalStateException();
         }
 
-        if (this.clientCertificate != null)
+        if (this.peerCertificate != null)
         {
             throw new TlsFatalAlert(AlertDescription.unexpected_message);
         }
 
-        this.clientCertificate = clientCertificate;
+        this.peerCertificate = clientCertificate;
 
         if (clientCertificate.isEmpty())
         {
@@ -439,7 +415,6 @@
     protected void receiveCertificateMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
         Certificate clientCertificate = Certificate.parse(buf);
 
         assertEmpty(buf);
@@ -450,22 +425,24 @@
     protected void receiveCertificateVerifyMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
-        byte[] clientCertificateSignature = TlsUtils.readOpaque16(buf);
+        DigitallySigned clientCertificateVerify = DigitallySigned.parse(getContext(), buf);
 
         assertEmpty(buf);
 
         // Verify the CertificateVerify message contains a correct signature.
         try
         {
-            TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType);
-            tlsSigner.init(getContext());
+            // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned
+            byte[] certificateVerifyHash = getCurrentPRFHash(getContext(), prepareFinishHash, null);
 
-            org.bouncycastle.asn1.x509.Certificate x509Cert = this.clientCertificate.getCertificateAt(0);
+            org.bouncycastle.asn1.x509.Certificate x509Cert = this.peerCertificate.getCertificateAt(0);
             SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo();
             AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo);
 
-            tlsSigner.verifyRawSignature(clientCertificateSignature, publicKey, this.certificateVerifyHash);
+            TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType);
+            tlsSigner.init(getContext());
+            tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(),
+                clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash);
         }
         catch (Exception e)
         {
@@ -476,42 +453,45 @@
     protected void receiveClientHelloMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
         ProtocolVersion client_version = TlsUtils.readVersion(buf);
         if (client_version.isDTLS())
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
-        /*
-         * Read the client random
-         */
         byte[] client_random = TlsUtils.readFully(32, buf);
 
+        /*
+         * TODO RFC 5077 3.4. If a ticket is presented by the client, the server MUST NOT attempt to
+         * use the Session ID in the ClientHello for stateful session resumption.
+         */
         byte[] sessionID = TlsUtils.readOpaque8(buf);
         if (sessionID.length > 32)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
-        }
-
-        int cipher_suites_length = TlsUtils.readUint16(buf);
-        if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
-        {
-            this.failWithError(AlertLevel.fatal, AlertDescription.decode_error);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
 
         /*
-         * NOTE: "If the session_id field is not empty (implying a session resumption request) this
-         * vector must include at least the cipher_suite from that session."
+         * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session
+         * resumption request), this vector MUST include at least the cipher_suite from that
+         * session.
          */
+        int cipher_suites_length = TlsUtils.readUint16(buf);
+        if (cipher_suites_length < 2 || (cipher_suites_length & 1) != 0)
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
         this.offeredCipherSuites = TlsUtils.readUint16Array(cipher_suites_length / 2, buf);
 
+        /*
+         * TODO RFC 5246 7.4.1.2. If the session_id field is not empty (implying a session
+         * resumption request), it MUST include the compression_method from that session.
+         */
         int compression_methods_length = TlsUtils.readUint8(buf);
         if (compression_methods_length < 1)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter);
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
         }
-
         this.offeredCompressionMethods = TlsUtils.readUint8Array(compression_methods_length, buf);
 
         /*
@@ -545,7 +525,7 @@
              * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, set the secure_renegotiation flag
              * to TRUE.
              */
-            if (arrayContains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
+            if (Arrays.contains(offeredCipherSuites, CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV))
             {
                 this.secure_renegotiation = true;
             }
@@ -554,22 +534,19 @@
              * The server MUST check if the "renegotiation_info" extension is included in the
              * ClientHello.
              */
-            if (clientExtensions != null)
+            byte[] renegExtData = TlsUtils.getExtensionData(clientExtensions, EXT_RenegotiationInfo);
+            if (renegExtData != null)
             {
-                byte[] renegExtValue = (byte[])clientExtensions.get(EXT_RenegotiationInfo);
-                if (renegExtValue != null)
-                {
-                    /*
-                     * If the extension is present, set secure_renegotiation flag to TRUE. The
-                     * server MUST then verify that the length of the "renegotiated_connection"
-                     * field is zero, and if it is not, MUST abort the handshake.
-                     */
-                    this.secure_renegotiation = true;
+                /*
+                 * If the extension is present, set secure_renegotiation flag to TRUE. The
+                 * server MUST then verify that the length of the "renegotiated_connection"
+                 * field is zero, and if it is not, MUST abort the handshake.
+                 */
+                this.secure_renegotiation = true;
 
-                    if (!Arrays.constantTimeAreEqual(renegExtValue, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
-                    {
-                        this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure);
-                    }
+                if (!Arrays.constantTimeAreEqual(renegExtData, createRenegotiationInfo(TlsUtils.EMPTY_BYTES)))
+                {
+                    throw new TlsFatalAlert(AlertDescription.handshake_failure);
                 }
             }
         }
@@ -585,81 +562,65 @@
     protected void receiveClientKeyExchangeMessage(ByteArrayInputStream buf)
         throws IOException
     {
-
         this.keyExchange.processClientKeyExchange(buf);
 
         assertEmpty(buf);
 
         establishMasterSecret(getContext(), keyExchange);
+        recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());
 
-        /*
-         * Initialize our cipher suite
-         */
-        recordStream.setPendingConnectionState(tlsServer.getCompression(), tlsServer.getCipher());
+        this.prepareFinishHash = recordStream.prepareToFinish();
 
-        if (expectCertificateVerifyMessage())
+        if (!expectSessionTicket)
         {
-            this.certificateVerifyHash = recordStream.getCurrentHash(null);
+            sendChangeCipherSpecMessage();
         }
     }
 
     protected void sendCertificateRequestMessage(CertificateRequest certificateRequest)
         throws IOException
     {
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_request);
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.certificate_request, buf);
+        certificateRequest.encode(message);
 
-        // Reserve space for length
-        TlsUtils.writeUint24(0, buf);
+        message.writeToRecordStream();
+    }
 
-        certificateRequest.encode(buf);
-        byte[] message = buf.toByteArray();
+    protected void sendCertificateStatusMessage(CertificateStatus certificateStatus)
+        throws IOException
+    {
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.certificate_status);
 
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
+        certificateStatus.encode(message);
 
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 
     protected void sendNewSessionTicketMessage(NewSessionTicket newSessionTicket)
         throws IOException
     {
-
         if (newSessionTicket == null)
         {
             throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.session_ticket, buf);
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.session_ticket);
 
-        // Reserve space for length
-        TlsUtils.writeUint24(0, buf);
+        newSessionTicket.encode(message);
 
-        newSessionTicket.encode(buf);
-        byte[] message = buf.toByteArray();
-
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
-
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 
     protected void sendServerHelloMessage()
         throws IOException
     {
-
-        ByteArrayOutputStream buf = new ByteArrayOutputStream();
-        TlsUtils.writeUint8(HandshakeType.server_hello, buf);
-
-        // Reserve space for length
-        TlsUtils.writeUint24(0, buf);
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.server_hello);
 
         ProtocolVersion server_version = tlsServer.getServerVersion();
         if (!server_version.isEqualOrEarlierVersionOf(getContext().getClientVersion()))
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
         recordStream.setReadVersion(server_version);
@@ -667,32 +628,34 @@
         recordStream.setRestrictReadVersion(true);
         getContext().setServerVersion(server_version);
 
-        TlsUtils.writeVersion(server_version, buf);
+        TlsUtils.writeVersion(server_version, message);
 
-        buf.write(this.securityParameters.serverRandom);
+        message.write(this.securityParameters.serverRandom);
 
         /*
          * The server may return an empty session_id to indicate that the session will not be cached
          * and therefore cannot be resumed.
          */
-        TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, buf);
+        TlsUtils.writeOpaque8(TlsUtils.EMPTY_BYTES, message);
 
-        this.selectedCipherSuite = tlsServer.getSelectedCipherSuite();
-        if (!arrayContains(this.offeredCipherSuites, this.selectedCipherSuite)
-            || this.selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
-            || this.selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
+        int selectedCipherSuite = tlsServer.getSelectedCipherSuite();
+        if (!Arrays.contains(this.offeredCipherSuites, selectedCipherSuite)
+            || selectedCipherSuite == CipherSuite.TLS_NULL_WITH_NULL_NULL
+            || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV)
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
+        securityParameters.cipherSuite = selectedCipherSuite;
 
-        this.selectedCompressionMethod = tlsServer.getSelectedCompressionMethod();
-        if (!arrayContains(this.offeredCompressionMethods, this.selectedCompressionMethod))
+        short selectedCompressionMethod = tlsServer.getSelectedCompressionMethod();
+        if (!Arrays.contains(this.offeredCompressionMethods, selectedCompressionMethod))
         {
-            this.failWithError(AlertLevel.fatal, AlertDescription.internal_error);
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
+        securityParameters.compressionAlgorithm = selectedCompressionMethod;
 
-        TlsUtils.writeUint16(this.selectedCipherSuite, buf);
-        TlsUtils.writeUint8(this.selectedCompressionMethod, buf);
+        TlsUtils.writeUint16(selectedCipherSuite, message);
+        TlsUtils.writeUint8(selectedCompressionMethod, message);
 
         this.serverExtensions = tlsServer.getServerExtensions();
 
@@ -701,9 +664,8 @@
          */
         if (this.secure_renegotiation)
         {
-
-            boolean noRenegExt = this.serverExtensions == null
-                || !this.serverExtensions.containsKey(EXT_RenegotiationInfo);
+            byte[] renegExtData = TlsUtils.getExtensionData(this.serverExtensions, EXT_RenegotiationInfo);
+            boolean noRenegExt = (null == renegExtData);
 
             if (noRenegExt)
             {
@@ -714,55 +676,81 @@
                  * because the client is signaling its willingness to receive the extension via the
                  * TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV.
                  */
-                if (this.serverExtensions == null)
-                {
-                    this.serverExtensions = new Hashtable();
-                }
 
                 /*
                  * If the secure_renegotiation flag is set to TRUE, the server MUST include an empty
                  * "renegotiation_info" extension in the ServerHello message.
                  */
+                this.serverExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(this.serverExtensions);
                 this.serverExtensions.put(EXT_RenegotiationInfo, createRenegotiationInfo(TlsUtils.EMPTY_BYTES));
             }
         }
 
+        /*
+         * TODO RFC 3546 2.3 If [...] the older session is resumed, then the server MUST ignore
+         * extensions appearing in the client hello, and send a server hello containing no
+         * extensions.
+         */
+
         if (this.serverExtensions != null)
         {
-            this.expectSessionTicket = serverExtensions.containsKey(EXT_SessionTicket);
-            writeExtensions(buf, this.serverExtensions);
+            this.securityParameters.maxFragmentLength = processMaxFragmentLengthExtension(clientExtensions,
+                this.serverExtensions, AlertDescription.internal_error);
+
+            this.securityParameters.truncatedHMac = TlsExtensionsUtils.hasTruncatedHMacExtension(this.serverExtensions);
+
+            /*
+             * TODO It's surprising that there's no provision to allow a 'fresh' CertificateStatus to be sent in
+             * a session resumption handshake.
+             */
+            this.allowCertificateStatus = !this.resumedSession
+                && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsExtensionsUtils.EXT_status_request,
+                    AlertDescription.internal_error);
+
+            this.expectSessionTicket = !this.resumedSession
+                && TlsUtils.hasExpectedEmptyExtensionData(this.serverExtensions, TlsProtocol.EXT_SessionTicket,
+                    AlertDescription.internal_error);
+
+            writeExtensions(message, this.serverExtensions);
         }
 
-        byte[] message = buf.toByteArray();
+        if (this.securityParameters.maxFragmentLength >= 0)
+        {
+            int plainTextLimit = 1 << (8 + this.securityParameters.maxFragmentLength);
+            recordStream.setPlaintextLimit(plainTextLimit);
+        }
 
-        // Patch actual length back in
-        TlsUtils.writeUint24(message.length - 4, message, 1);
+        securityParameters.prfAlgorithm = getPRFAlgorithm(getContext(), securityParameters.getCipherSuite());
 
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        /*
+         * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length has
+         * a verify_data_length equal to 12. This includes all existing cipher suites.
+         */
+        securityParameters.verifyDataLength = 12;
+
+        message.writeToRecordStream();
+
+        this.recordStream.notifyHelloComplete();
     }
 
     protected void sendServerHelloDoneMessage()
         throws IOException
     {
-
         byte[] message = new byte[4];
         TlsUtils.writeUint8(HandshakeType.server_hello_done, message, 0);
         TlsUtils.writeUint24(0, message, 1);
 
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        writeHandshakeMessage(message, 0, message.length);
     }
 
     protected void sendServerKeyExchangeMessage(byte[] serverKeyExchange)
         throws IOException
     {
-        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        HandshakeMessage message = new HandshakeMessage(HandshakeType.server_key_exchange, serverKeyExchange.length);
 
-        TlsUtils.writeUint8(HandshakeType.server_key_exchange, bos);
-        TlsUtils.writeUint24(serverKeyExchange.length, bos);
-        bos.write(serverKeyExchange);
-        byte[] message = bos.toByteArray();
+        message.write(serverKeyExchange);
 
-        safeWriteRecord(ContentType.handshake, message, 0, message.length);
+        message.writeToRecordStream();
     }
 
     protected boolean expectCertificateVerifyMessage()
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java
new file mode 100644
index 0000000..9b5ad46
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSession.java
@@ -0,0 +1,12 @@
+package org.bouncycastle.crypto.tls;
+
+public interface TlsSession
+{
+    SessionParameters exportSessionParameters();
+
+    byte[] getSessionID();
+
+    void invalidate();
+
+    boolean isResumable();
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
new file mode 100644
index 0000000..615c442
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSessionImpl.java
@@ -0,0 +1,48 @@
+package org.bouncycastle.crypto.tls;
+
+import org.bouncycastle.util.Arrays;
+
+class TlsSessionImpl implements TlsSession
+{
+    final byte[] sessionID;
+    SessionParameters sessionParameters;
+
+    TlsSessionImpl(byte[] sessionID, SessionParameters sessionParameters)
+    {
+        if (sessionID == null)
+        {
+            throw new IllegalArgumentException("'sessionID' cannot be null");
+        }
+        if (sessionID.length < 1 || sessionID.length > 32)
+        {
+            throw new IllegalArgumentException("'sessionID' must have length between 1 and 32 bytes, inclusive");
+        }
+
+        this.sessionID = Arrays.clone(sessionID);
+        this.sessionParameters = sessionParameters;
+    }
+
+    public synchronized SessionParameters exportSessionParameters()
+    {
+        return this.sessionParameters == null ? null : this.sessionParameters.copy();
+    }
+
+    public synchronized byte[] getSessionID()
+    {
+        return sessionID;
+    }
+
+    public synchronized void invalidate()
+    {
+        if (this.sessionParameters != null)
+        {
+            this.sessionParameters.clear();
+            this.sessionParameters = null;
+        }
+    }
+
+    public synchronized boolean isResumable()
+    {
+        return this.sessionParameters != null;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
index 2b61507..68826d2 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSigner.java
@@ -6,18 +6,29 @@
 
 public interface TlsSigner
 {
-
     void init(TlsContext context);
 
     byte[] generateRawSignature(AsymmetricKeyParameter privateKey, byte[] md5AndSha1)
         throws CryptoException;
 
+    byte[] generateRawSignature(SignatureAndHashAlgorithm algorithm,
+        AsymmetricKeyParameter privateKey, byte[] hash)
+        throws CryptoException;
+
     boolean verifyRawSignature(byte[] sigBytes, AsymmetricKeyParameter publicKey, byte[] md5AndSha1)
         throws CryptoException;
 
+    boolean verifyRawSignature(SignatureAndHashAlgorithm algorithm, byte[] sigBytes,
+        AsymmetricKeyParameter publicKey, byte[] hash)
+        throws CryptoException;
+
     Signer createSigner(AsymmetricKeyParameter privateKey);
 
+    Signer createSigner(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter privateKey);
+
     Signer createVerifyer(AsymmetricKeyParameter publicKey);
 
+    Signer createVerifyer(SignatureAndHashAlgorithm algorithm, AsymmetricKeyParameter publicKey);
+
     boolean isValidPublicKey(AsymmetricKeyParameter publicKey);
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
index 7067fa2..39ee99c 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsSignerCredentials.java
@@ -5,6 +5,8 @@
 public interface TlsSignerCredentials
     extends TlsCredentials
 {
-    byte[] generateCertificateSignature(byte[] md5andsha1)
+    byte[] generateCertificateSignature(byte[] hash)
         throws IOException;
+
+    SignatureAndHashAlgorithm getSignatureAndHashAlgorithm();
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
index 1755c2d..178731d 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsStreamCipher.java
@@ -11,6 +11,8 @@
 public class TlsStreamCipher
     implements TlsCipher
 {
+    private static boolean encryptThenMAC = false;
+
     protected TlsContext context;
 
     protected StreamCipher encryptCipher;
@@ -20,11 +22,9 @@
     protected TlsMac readMac;
 
     public TlsStreamCipher(TlsContext context, StreamCipher clientWriteCipher,
-                           StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest,
-                           int cipherKeySize)
-        throws IOException
+        StreamCipher serverWriteCipher, Digest clientWriteDigest, Digest serverWriteDigest,
+        int cipherKeySize) throws IOException
     {
-
         boolean isServer = context.isServer();
 
         this.context = context;
@@ -89,38 +89,75 @@
 
     public byte[] encodePlaintext(long seqNo, short type, byte[] plaintext, int offset, int len)
     {
-        byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+        /*
+         * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That
+         * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+         * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+         * of the 16-bit epoch with the 48-bit sequence number.
+         */
 
-        byte[] outbuf = new byte[len + mac.length];
+        byte[] outBuf = new byte[len + writeMac.getSize()];
 
-        encryptCipher.processBytes(plaintext, offset, len, outbuf, 0);
-        encryptCipher.processBytes(mac, 0, mac.length, outbuf, len);
+        encryptCipher.processBytes(plaintext, offset, len, outBuf, 0);
 
-        return outbuf;
+        if (encryptThenMAC)
+        {
+            byte[] mac = writeMac.calculateMac(seqNo, type, outBuf, 0, len);
+            System.arraycopy(mac, 0, outBuf, len, mac.length);
+        }
+        else
+        {
+            byte[] mac = writeMac.calculateMac(seqNo, type, plaintext, offset, len);
+            encryptCipher.processBytes(mac, 0, mac.length, outBuf, len);
+        }
+
+        return outBuf;
     }
 
     public byte[] decodeCiphertext(long seqNo, short type, byte[] ciphertext, int offset, int len)
         throws IOException
     {
+        /*
+         * TODO[draft-josefsson-salsa20-tls-02] Note that Salsa20 requires a 64-bit nonce. That
+         * nonce is updated on the encryption of every TLS record, and is set to be the 64-bit TLS
+         * record sequence number. In case of DTLS the 64-bit nonce is formed as the concatenation
+         * of the 16-bit epoch with the 48-bit sequence number.
+         */
+
         int macSize = readMac.getSize();
         if (len < macSize)
         {
             throw new TlsFatalAlert(AlertDescription.decode_error);
         }
 
-        byte[] deciphered = new byte[len];
-        decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0);
+        int plaintextLength = len - macSize;
 
-        int macInputLen = len - macSize;
+        if (encryptThenMAC)
+        {
+            int ciphertextEnd = offset + len;
+            checkMAC(seqNo, type, ciphertext, ciphertextEnd - macSize, ciphertextEnd, ciphertext, offset, plaintextLength);
+            byte[] deciphered = new byte[plaintextLength];
+            decryptCipher.processBytes(ciphertext, offset, plaintextLength, deciphered, 0);
+            return deciphered;
+        }
+        else
+        {
+            byte[] deciphered = new byte[len];
+            decryptCipher.processBytes(ciphertext, offset, len, deciphered, 0);
+            checkMAC(seqNo, type, deciphered, plaintextLength, len, deciphered, 0, plaintextLength);
+            return Arrays.copyOfRange(deciphered, 0, plaintextLength);
+        }
+    }
 
-        byte[] receivedMac = Arrays.copyOfRange(deciphered, macInputLen, len);
-        byte[] computedMac = readMac.calculateMac(seqNo, type, deciphered, 0, macInputLen);
+    private void checkMAC(long seqNo, short type, byte[] recBuf, int recStart, int recEnd, byte[] calcBuf, int calcOff, int calcLen)
+        throws IOException
+    {
+        byte[] receivedMac = Arrays.copyOfRange(recBuf, recStart, recEnd);
+        byte[] computedMac = readMac.calculateMac(seqNo, type, calcBuf, calcOff, calcLen);
 
         if (!Arrays.constantTimeAreEqual(receivedMac, computedMac))
         {
             throw new TlsFatalAlert(AlertDescription.bad_record_mac);
         }
-
-        return Arrays.copyOfRange(deciphered, 0, macInputLen);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
index 8b16210..dae9ff5 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/TlsUtils.java
@@ -9,7 +9,10 @@
 import java.util.Hashtable;
 import java.util.Vector;
 
+import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.Extensions;
@@ -44,11 +47,72 @@
 
     public static final Integer EXT_signature_algorithms = Integers.valueOf(ExtensionType.signature_algorithms);
 
+    public static void checkUint8(short i) throws IOException
+    {
+        if (!isValidUint8(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    public static void checkUint8(int i) throws IOException
+    {
+        if (!isValidUint8(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    public static void checkUint16(int i) throws IOException
+    {
+        if (!isValidUint16(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    public static void checkUint24(int i) throws IOException
+    {
+        if (!isValidUint24(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    public static void checkUint32(long i) throws IOException
+    {
+        if (!isValidUint32(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    public static void checkUint48(long i) throws IOException
+    {
+        if (!isValidUint48(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
+    public static void checkUint64(long i) throws IOException
+    {
+        if (!isValidUint64(i))
+        {
+            throw new TlsFatalAlert(AlertDescription.internal_error);
+        }
+    }
+
     public static boolean isValidUint8(short i)
     {
         return (i & 0xFF) == i;
     }
 
+    public static boolean isValidUint8(int i)
+    {
+        return (i & 0xFF) == i;
+    }
+
     public static boolean isValidUint16(int i)
     {
         return (i & 0xFFFF) == i;
@@ -74,17 +138,43 @@
         return true;
     }
 
+    public static boolean isSSL(TlsContext context)
+    {
+        return context.getServerVersion().isSSL();
+    }
+
+    public static boolean isTLSv11(TlsContext context)
+    {
+        return ProtocolVersion.TLSv11.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion());
+    }
+
+    public static boolean isTLSv12(TlsContext context)
+    {
+        return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(context.getServerVersion().getEquivalentTLSVersion());
+    }
+
     public static void writeUint8(short i, OutputStream output)
         throws IOException
     {
         output.write(i);
     }
 
+    public static void writeUint8(int i, OutputStream output)
+        throws IOException
+    {
+        output.write(i);
+    }
+
     public static void writeUint8(short i, byte[] buf, int offset)
     {
         buf[offset] = (byte)i;
     }
 
+    public static void writeUint8(int i, byte[] buf, int offset)
+    {
+        buf[offset] = (byte)i;
+    }
+
     public static void writeUint16(int i, OutputStream output)
         throws IOException
     {
@@ -130,6 +220,17 @@
         buf[offset + 3] = (byte)(i);
     }
 
+    public static void writeUint48(long i, OutputStream output)
+        throws IOException
+    {
+        output.write((byte)(i >> 40));
+        output.write((byte)(i >> 32));
+        output.write((byte)(i >> 24));
+        output.write((byte)(i >> 16));
+        output.write((byte)(i >> 8));
+        output.write((byte)(i));
+    }
+
     public static void writeUint48(long i, byte[] buf, int offset)
     {
         buf[offset] = (byte)(i >> 40);
@@ -143,14 +244,14 @@
     public static void writeUint64(long i, OutputStream output)
         throws IOException
     {
-        output.write((int)(i >> 56));
-        output.write((int)(i >> 48));
-        output.write((int)(i >> 40));
-        output.write((int)(i >> 32));
-        output.write((int)(i >> 24));
-        output.write((int)(i >> 16));
-        output.write((int)(i >> 8));
-        output.write((int)(i));
+        output.write((byte)(i >> 56));
+        output.write((byte)(i >> 48));
+        output.write((byte)(i >> 40));
+        output.write((byte)(i >> 32));
+        output.write((byte)(i >> 24));
+        output.write((byte)(i >> 16));
+        output.write((byte)(i >> 8));
+        output.write((byte)(i));
     }
 
     public static void writeUint64(long i, byte[] buf, int offset)
@@ -168,13 +269,15 @@
     public static void writeOpaque8(byte[] buf, OutputStream output)
         throws IOException
     {
-        writeUint8((short)buf.length, output);
+        checkUint8(buf.length);
+        writeUint8(buf.length, output);
         output.write(buf);
     }
 
     public static void writeOpaque16(byte[] buf, OutputStream output)
         throws IOException
     {
+        checkUint16(buf.length);
         writeUint16(buf.length, output);
         output.write(buf);
     }
@@ -182,6 +285,7 @@
     public static void writeOpaque24(byte[] buf, OutputStream output)
         throws IOException
     {
+        checkUint24(buf.length);
         writeUint24(buf.length, output);
         output.write(buf);
     }
@@ -195,6 +299,32 @@
         }
     }
 
+    public static void writeUint8Array(short[] uints, byte[] buf, int offset)
+        throws IOException
+    {
+        for (int i = 0; i < uints.length; ++i)
+        {
+            writeUint8(uints[i], buf, offset);
+            ++offset;
+        }
+    }
+
+    public static void writeUint8ArrayWithUint8Length(short[] uints, OutputStream output)
+        throws IOException
+    {
+        checkUint8(uints.length);
+        writeUint8(uints.length, output);
+        writeUint8Array(uints, output);
+    }
+
+    public static void writeUint8ArrayWithUint8Length(short[] uints, byte[] buf, int offset)
+        throws IOException
+    {
+        checkUint8(uints.length);
+        writeUint8(uints.length, buf, offset);
+        writeUint8Array(uints, buf, offset + 1);
+    }
+
     public static void writeUint16Array(int[] uints, OutputStream output)
         throws IOException
     {
@@ -204,6 +334,56 @@
         }
     }
 
+    public static void writeUint16Array(int[] uints, byte[] buf, int offset)
+        throws IOException
+    {
+        for (int i = 0; i < uints.length; ++i)
+        {
+            writeUint16(uints[i], buf, offset);
+            offset += 2;
+        }
+    }
+
+    public static void writeUint16ArrayWithUint16Length(int[] uints, OutputStream output)
+        throws IOException
+    {
+        int length = 2 * uints.length;
+        checkUint16(length);
+        writeUint16(length, output);
+        writeUint16Array(uints, output);
+    }
+
+    public static void writeUint16ArrayWithUint16Length(int[] uints, byte[] buf, int offset)
+        throws IOException
+    {
+        int length = 2 * uints.length;
+        checkUint16(length);
+        writeUint16(length, buf, offset);
+        writeUint16Array(uints, buf, offset + 2);
+    }
+
+    public static byte[] encodeOpaque8(byte[] buf)
+        throws IOException
+    {
+        checkUint8(buf.length);
+        return Arrays.prepend(buf, (byte)buf.length);
+    }
+
+    public static byte[] encodeUint8ArrayWithUint8Length(short[] uints) throws IOException
+    {
+        byte[] result = new byte[1 + uints.length];
+        writeUint8ArrayWithUint8Length(uints, result, 0);
+        return result;
+    }
+
+    public static byte[] encodeUint16ArrayWithUint16Length(int[] uints) throws IOException
+    {
+        int length = 2 * uints.length;
+        byte[] result = new byte[2 + length];
+        writeUint16ArrayWithUint16Length(uints, result, 0);
+        return result;
+    }
+
     public static short readUint8(InputStream input)
         throws IOException
     {
@@ -297,6 +477,26 @@
         return ((long)(hi & 0xffffffffL) << 24) | (long)(lo & 0xffffffffL);
     }
 
+    public static byte[] readAllOrNothing(int length, InputStream input)
+        throws IOException
+    {
+        if (length < 1)
+        {
+            return EMPTY_BYTES;
+        }
+        byte[] buf = new byte[length];
+        int read = Streams.readFully(input, buf);
+        if (read == 0)
+        {
+            return null;
+        }
+        if (read != length)
+        {
+            throw new EOFException();
+        }
+        return buf;
+    }
+
     public static byte[] readFully(int length, InputStream input)
         throws IOException
     {
@@ -383,6 +583,12 @@
         return ProtocolVersion.get(i1, i2);
     }
 
+    public static int readVersionRaw(byte[] buf, int offset)
+        throws IOException
+    {
+        return (buf[offset] << 8) | buf[offset + 1];
+    }
+
     public static int readVersionRaw(InputStream input)
         throws IOException
     {
@@ -395,6 +601,36 @@
         return (i1 << 8) | i2;
     }
 
+    public static ASN1Primitive readASN1Object(byte[] encoding) throws IOException
+    {
+        ASN1InputStream asn1 = new ASN1InputStream(encoding);
+        ASN1Primitive result = asn1.readObject();
+        if (null == result)
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+        if (null != asn1.readObject())
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+        return result;
+    }
+
+    public static ASN1Primitive readDERObject(byte[] encoding) throws IOException
+    {
+        /*
+         * NOTE: The current ASN.1 parsing code can't enforce DER-only parsing, but since DER is
+         * canonical, we can check it by re-encoding the result and comparing to the original.
+         */
+        ASN1Primitive result = readASN1Object(encoding);
+        byte[] check = result.getEncoded(ASN1Encoding.DER);
+        if (!Arrays.areEqual(check, encoding))
+        {
+            throw new TlsFatalAlert(AlertDescription.decode_error);
+        }
+        return result;
+    }
+
     public static void writeGMTUnixTime(byte[] buf, int offset)
     {
         int t = (int)(System.currentTimeMillis() / 1000L);
@@ -412,7 +648,6 @@
     }
 
     public static void writeVersion(ProtocolVersion version, byte[] buf, int offset)
-        throws IOException
     {
         buf[offset] = (byte)version.getMajorVersion();
         buf[offset + 1] = (byte)version.getMinorVersion();
@@ -433,6 +668,31 @@
         return vectorOfOne(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.rsa));
     }
 
+    public static byte[] getExtensionData(Hashtable extensions, Integer extensionType)
+    {
+        return extensions == null ? null : (byte[])extensions.get(extensionType);
+    }
+
+    public static boolean hasExpectedEmptyExtensionData(Hashtable extensions, Integer extensionType,
+        short alertDescription) throws IOException
+    {
+        byte[] extension_data = getExtensionData(extensions, extensionType);
+        if (extension_data == null)
+        {
+            return false;
+        }
+        if (extension_data.length != 0)
+        {
+            throw new TlsFatalAlert(alertDescription);
+        }
+        return true;
+    }
+
+    public static TlsSession importSession(byte[] sessionID, SessionParameters sessionParameters)
+    {
+        return new TlsSessionImpl(sessionID, sessionParameters);
+    }
+
     public static boolean isSignatureAlgorithmsExtensionAllowed(ProtocolVersion clientVersion)
     {
         return ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(clientVersion.getEquivalentTLSVersion());
@@ -461,17 +721,8 @@
     public static Vector getSignatureAlgorithmsExtension(Hashtable extensions)
         throws IOException
     {
-
-        if (extensions == null)
-        {
-            return null;
-        }
-        byte[] extensionValue = (byte[])extensions.get(EXT_signature_algorithms);
-        if (extensionValue == null)
-        {
-            return null;
-        }
-        return readSignatureAlgorithmsExtension(extensionValue);
+        byte[] extensionData = getExtensionData(extensions, EXT_signature_algorithms);
+        return extensionData == null ? null : readSignatureAlgorithmsExtension(extensionData);
     }
 
     /**
@@ -484,61 +735,94 @@
     public static byte[] createSignatureAlgorithmsExtension(Vector supportedSignatureAlgorithms)
         throws IOException
     {
-
-        if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.size() < 1 || supportedSignatureAlgorithms.size() >= (1 << 15))
-        {
-            throw new IllegalArgumentException(
-                "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)");
-        }
-
         ByteArrayOutputStream buf = new ByteArrayOutputStream();
 
         // supported_signature_algorithms
-        TlsUtils.writeUint16(2 * supportedSignatureAlgorithms.size(), buf);
-        for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
-        {
-            SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i);
-            entry.encode(buf);
-        }
+        encodeSupportedSignatureAlgorithms(supportedSignatureAlgorithms, false, buf);
 
         return buf.toByteArray();
     }
 
     /**
-     * Read a 'signature_algorithms' extension value.
+     * Read 'signature_algorithms' extension data.
      *
-     * @param extensionValue The extension value.
+     * @param extensionData The extension data.
      * @return A {@link Vector} containing at least 1 {@link SignatureAndHashAlgorithm}.
      * @throws IOException
      */
-    public static Vector readSignatureAlgorithmsExtension(byte[] extensionValue)
+    public static Vector readSignatureAlgorithmsExtension(byte[] extensionData)
         throws IOException
     {
-
-        if (extensionValue == null)
+        if (extensionData == null)
         {
-            throw new IllegalArgumentException("'extensionValue' cannot be null");
+            throw new IllegalArgumentException("'extensionData' cannot be null");
         }
 
-        ByteArrayInputStream buf = new ByteArrayInputStream(extensionValue);
+        ByteArrayInputStream buf = new ByteArrayInputStream(extensionData);
 
         // supported_signature_algorithms
-        int length = TlsUtils.readUint16(buf);
+        Vector supported_signature_algorithms = parseSupportedSignatureAlgorithms(false, buf);
+
+        TlsProtocol.assertEmpty(buf);
+
+        return supported_signature_algorithms;
+    }
+
+    public static void encodeSupportedSignatureAlgorithms(Vector supportedSignatureAlgorithms, boolean allowAnonymous,
+        OutputStream output) throws IOException
+    {
+        if (supportedSignatureAlgorithms == null || supportedSignatureAlgorithms.size() < 1
+            || supportedSignatureAlgorithms.size() >= (1 << 15))
+        {
+            throw new IllegalArgumentException(
+                "'supportedSignatureAlgorithms' must have length from 1 to (2^15 - 1)");
+        }
+
+        // supported_signature_algorithms
+        int length = 2 * supportedSignatureAlgorithms.size();
+        TlsUtils.checkUint16(length);
+        TlsUtils.writeUint16(length, output);
+        for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
+        {
+            SignatureAndHashAlgorithm entry = (SignatureAndHashAlgorithm)supportedSignatureAlgorithms.elementAt(i);
+            if (!allowAnonymous && entry.getSignature() == SignatureAlgorithm.anonymous)
+            {
+                /*
+                 * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+                 * in Section 7.4.3. It MUST NOT appear in this extension.
+                 */
+                throw new IllegalArgumentException(
+                    "SignatureAlgorithm.anonymous MUST NOT appear in the signature_algorithms extension");
+            }
+            entry.encode(output);
+        }
+    }
+
+    public static Vector parseSupportedSignatureAlgorithms(boolean allowAnonymous, InputStream input)
+        throws IOException
+    {
+        // supported_signature_algorithms
+        int length = TlsUtils.readUint16(input);
         if (length < 2 || (length & 1) != 0)
         {
             throw new TlsFatalAlert(AlertDescription.decode_error);
         }
         int count = length / 2;
-        Vector result = new Vector(count);
+        Vector supportedSignatureAlgorithms = new Vector(count);
         for (int i = 0; i < count; ++i)
         {
-            SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(buf);
-            result.addElement(entry);
+            SignatureAndHashAlgorithm entry = SignatureAndHashAlgorithm.parse(input);
+            if (!allowAnonymous && entry.getSignature() == SignatureAlgorithm.anonymous)
+            {
+                /*
+                 * RFC 5246 7.4.1.4.1 The "anonymous" value is meaningless in this context but used
+                 * in Section 7.4.3. It MUST NOT appear in this extension.
+                 */
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+            supportedSignatureAlgorithms.addElement(entry);
         }
-
-        TlsProtocol.assertEmpty(buf);
-
-        return result;
+        return supportedSignatureAlgorithms;
     }
 
     public static byte[] PRF(TlsContext context, byte[] secret, String asciiLabel, byte[] seed, int size)
@@ -557,12 +841,7 @@
 
         if (prfAlgorithm == PRFAlgorithm.tls_prf_legacy)
         {
-            if (!ProtocolVersion.TLSv12.isEqualOrEarlierVersionOf(version.getEquivalentTLSVersion()))
-            {
-                return PRF_legacy(secret, label, labelSeed, size);
-            }
-
-            prfAlgorithm = PRFAlgorithm.tls_prf_sha256;
+            return PRF_legacy(secret, label, labelSeed, size);
         }
 
         Digest prfDigest = createPRFHash(prfAlgorithm);
@@ -646,7 +925,7 @@
         byte[] seed = concat(securityParameters.getServerRandom(),
             securityParameters.getClientRandom());
 
-        if (context.getServerVersion().isSSL())
+        if (isSSL(context))
         {
             return calculateKeyBlock_SSL(master_secret, seed, size);
         }
@@ -690,7 +969,7 @@
         SecurityParameters securityParameters = context.getSecurityParameters();
         byte[] seed = concat(securityParameters.getClientRandom(), securityParameters.getServerRandom());
 
-        if (context.getServerVersion().isSSL())
+        if (isSSL(context))
         {
             return calculateMasterSecret_SSL(pre_master_secret, seed);
         }
@@ -729,7 +1008,7 @@
 
     static byte[] calculateVerifyData(TlsContext context, String asciiLabel, byte[] handshakeHash)
     {
-        if (context.getServerVersion().isSSL())
+        if (isSSL(context))
         {
             return handshakeHash;
         }
@@ -741,7 +1020,7 @@
         return PRF(context, master_secret, asciiLabel, handshakeHash, verify_data_length);
     }
 
-    public static final Digest createHash(int hashAlgorithm)
+    public static final Digest createHash(short hashAlgorithm)
     {
         switch (hashAlgorithm)
         {
@@ -762,7 +1041,7 @@
         }
     }
 
-    public static final Digest cloneHash(int hashAlgorithm, Digest hash)
+    public static final Digest cloneHash(short hashAlgorithm, Digest hash)
     {
         switch (hashAlgorithm)
         {
@@ -820,7 +1099,7 @@
         }
     }
 
-    public static ASN1ObjectIdentifier getOIDForHashAlgorithm(int hashAlgorithm)
+    public static ASN1ObjectIdentifier getOIDForHashAlgorithm(short hashAlgorithm)
     {
         switch (hashAlgorithm)
         {
@@ -912,6 +1191,20 @@
         throw new TlsFatalAlert(AlertDescription.unsupported_certificate);
     }
 
+    static void trackHashAlgorithms(TlsHandshakeHash handshakeHash, Vector supportedSignatureAlgorithms)
+    {
+        if (supportedSignatureAlgorithms != null)
+        {
+            for (int i = 0; i < supportedSignatureAlgorithms.size(); ++i)
+            {
+                SignatureAndHashAlgorithm signatureAndHashAlgorithm = (SignatureAndHashAlgorithm)
+                    supportedSignatureAlgorithms.elementAt(i);
+                short hashAlgorithm = signatureAndHashAlgorithm.getHash();
+                handshakeHash.trackHashAlgorithm(hashAlgorithm);
+            }
+        }
+    }
+
     public static boolean hasSigningCapability(short clientCertificateType)
     {
         switch (clientCertificateType)
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
index f3dd59e..d5f0769 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/UDPTransport.java
@@ -7,18 +7,16 @@
 public class UDPTransport
     implements DatagramTransport
 {
+    protected final static int MIN_IP_OVERHEAD = 20;
+    protected final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
+    protected final static int UDP_OVERHEAD = 8;
 
-    private final static int MIN_IP_OVERHEAD = 20;
-    private final static int MAX_IP_OVERHEAD = MIN_IP_OVERHEAD + 64;
-    private final static int UDP_OVERHEAD = 8;
-
-    private final DatagramSocket socket;
-    private final int receiveLimit, sendLimit;
+    protected final DatagramSocket socket;
+    protected final int receiveLimit, sendLimit;
 
     public UDPTransport(DatagramSocket socket, int mtu)
         throws IOException
     {
-
         if (!socket.isBound() || !socket.isConnected())
         {
             throw new IllegalArgumentException("'socket' must be bound and connected");
@@ -62,7 +60,7 @@
              * the DTLS implementation SHOULD generate an error, thus avoiding sending a packet
              * which will be fragmented."
              */
-            // TODO Exception
+            throw new TlsFatalAlert(AlertDescription.internal_error);
         }
 
         DatagramPacket packet = new DatagramPacket(buf, off, len);
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java b/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java
new file mode 100644
index 0000000..c32a904
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/tls/URLAndHash.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.crypto.tls;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Strings;
+
+/**
+ * RFC 6066 5.
+ */
+public class URLAndHash
+{
+    protected String url;
+    protected byte[] sha1Hash;
+
+    public URLAndHash(String url, byte[] sha1Hash)
+    {
+        if (url == null || url.length() < 1 || url.length() >= (1 << 16))
+        {
+            throw new IllegalArgumentException("'url' must have length from 1 to (2^16 - 1)");
+        }
+        if (sha1Hash != null && sha1Hash.length != 20)
+        {
+            throw new IllegalArgumentException("'sha1Hash' must have length == 20, if present");
+        }
+
+        this.url = url;
+        this.sha1Hash = sha1Hash;
+    }
+
+    public String getURL()
+    {
+        return url;
+    }
+
+    public byte[] getSHA1Hash()
+    {
+        return sha1Hash;
+    }
+
+    /**
+     * Encode this {@link URLAndHash} to an {@link OutputStream}.
+     *
+     * @param output the {@link OutputStream} to encode to.
+     * @throws IOException
+     */
+    public void encode(OutputStream output)
+        throws IOException
+    {
+        byte[] urlEncoding = Strings.toByteArray(this.url);
+        TlsUtils.writeOpaque16(urlEncoding, output);
+
+        if (this.sha1Hash == null)
+        {
+            TlsUtils.writeUint8(0, output);
+        }
+        else
+        {
+            TlsUtils.writeUint8(1, output);
+            output.write(this.sha1Hash);
+        }
+    }
+
+    /**
+     * Parse a {@link URLAndHash} from an {@link InputStream}.
+     * 
+     * @param context
+     *            the {@link TlsContext} of the current connection.
+     * @param input
+     *            the {@link InputStream} to parse from.
+     * @return a {@link URLAndHash} object.
+     * @throws IOException
+     */
+    public static URLAndHash parse(TlsContext context, InputStream input)
+        throws IOException
+    {
+        byte[] urlEncoding = TlsUtils.readOpaque16(input);
+        if (urlEncoding.length < 1)
+        {
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+        String url = Strings.fromByteArray(urlEncoding);
+
+        byte[] sha1Hash = null;
+        short padding = TlsUtils.readUint8(input);
+        switch (padding)
+        {
+        case 0:
+            if (TlsUtils.isTLSv12(context))
+            {
+                throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+            }
+            break;
+        case 1:
+            sha1Hash = TlsUtils.readFully(20, input);
+            break;
+        default:
+            throw new TlsFatalAlert(AlertDescription.illegal_parameter);
+        }
+
+        return new URLAndHash(url, sha1Hash);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
index bfa304b..6bf3399 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyFactory.java
@@ -10,7 +10,6 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
 import org.bouncycastle.asn1.oiw.ElGamalParameter;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.DHParameter;
@@ -18,11 +17,9 @@
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
 import org.bouncycastle.asn1.sec.ECPrivateKey;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -130,22 +127,7 @@
             if (params.isNamedCurve())
             {
                 ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
-                x9 = X962NamedCurves.getByOID(oid);
-
-                if (x9 == null)
-                {
-                    x9 = SECNamedCurves.getByOID(oid);
-
-                    if (x9 == null)
-                    {
-                        x9 = NISTNamedCurves.getByOID(oid);
-
-                        if (x9 == null)
-                        {
-                            x9 = TeleTrusTNamedCurves.getByOID(oid);
-                        }
-                    }
-                }
+                x9 = ECNamedCurveTable.getByOID(oid);
             }
             else
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
index ab52802..7b06c3f 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PrivateKeyInfoFactory.java
@@ -2,17 +2,23 @@
 
 import java.io.IOException;
 
+import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAPrivateKey;
+import org.bouncycastle.asn1.sec.ECPrivateKey;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
+import org.bouncycastle.asn1.x9.X962Parameters;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
 import org.bouncycastle.crypto.params.DSAParameters;
 import org.bouncycastle.crypto.params.DSAPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 
@@ -43,6 +49,31 @@
 
             return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_dsa, new DSAParameter(params.getP(), params.getQ(), params.getG())), new ASN1Integer(priv.getX()));
         }
+        else if (privateKey instanceof ECPrivateKeyParameters)
+        {
+            ECPrivateKeyParameters priv = (ECPrivateKeyParameters)privateKey;
+            ECDomainParameters domainParams = priv.getParameters();
+            ASN1Encodable params;
+
+            // TODO: need to handle named curves
+            if (domainParams == null)
+            {
+                params = new X962Parameters(DERNull.INSTANCE);      // Implicitly CA
+            }
+            else
+            {
+                X9ECParameters ecP = new X9ECParameters(
+                    domainParams.getCurve(),
+                    domainParams.getG(),
+                    domainParams.getN(),
+                    domainParams.getH(),
+                    domainParams.getSeed());
+
+                params = new X962Parameters(ecP);
+            }
+
+            return new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), new ECPrivateKey(priv.getD(), params));
+        }
         else
         {
             throw new IOException("key parameters not recognised.");
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
index 343bbd3..2d2927b 100644
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
+++ b/bcprov/src/main/java/org/bouncycastle/crypto/util/PublicKeyFactory.java
@@ -12,14 +12,11 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DEROctetString;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
 import org.bouncycastle.asn1.oiw.ElGamalParameter;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.DHParameter;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DSAParameter;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -27,7 +24,7 @@
 import org.bouncycastle.asn1.x9.DHDomainParameters;
 import org.bouncycastle.asn1.x9.DHPublicKey;
 import org.bouncycastle.asn1.x9.DHValidationParms;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ECPoint;
@@ -160,29 +157,13 @@
         }
         else if (algId.getAlgorithm().equals(X9ObjectIdentifiers.id_ecPublicKey))
         {
-            X962Parameters params = new X962Parameters(
-                (ASN1Primitive)algId.getParameters());
+            X962Parameters params = X962Parameters.getInstance(algId.getParameters());
 
             X9ECParameters x9;
             if (params.isNamedCurve())
             {
                 ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
-                x9 = X962NamedCurves.getByOID(oid);
-
-                if (x9 == null)
-                {
-                    x9 = SECNamedCurves.getByOID(oid);
-
-                    if (x9 == null)
-                    {
-                        x9 = NISTNamedCurves.getByOID(oid);
-
-                        if (x9 == null)
-                        {
-                            x9 = TeleTrusTNamedCurves.getByOID(oid);
-                        }
-                    }
-                }
+                x9 = ECNamedCurveTable.getByOID(oid);
             }
             else
             {
diff --git a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html b/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html
deleted file mode 100644
index 787b892..0000000
--- a/bcprov/src/main/java/org/bouncycastle/crypto/util/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Some general utility/conversion classes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java b/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java
index b9e2232..ab898a9 100644
--- a/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java
+++ b/bcprov/src/main/java/org/bouncycastle/i18n/LocaleString.java
@@ -15,6 +15,12 @@
     {
         super(resource, id, encoding);
     }
+
+    public LocaleString(String resource, String id, String encoding, Object[] arguments)
+        throws NullPointerException, UnsupportedEncodingException
+    {
+        super(resource, id, encoding, arguments);
+    }
     
     public String getLocaleString(Locale locale)
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/JcaJceUtils.java b/bcprov/src/main/java/org/bouncycastle/jcajce/JcaJceUtils.java
new file mode 100644
index 0000000..d7677f3
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/JcaJceUtils.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.jcajce;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+
+import org.bouncycastle.asn1.ASN1Encodable;
+import org.bouncycastle.asn1.ASN1Primitive;
+
+public class JcaJceUtils
+{
+    private JcaJceUtils()
+    {
+
+    }
+
+    /**
+     * Extract an ASN.1 encodable from an AlgorithmParameters object.
+     *
+     * @param params the object to get the encoding used to create the return value.
+     * @return an ASN.1 object representing the primitives making up the params parameter.
+     * @throws IOException if an encoding cannot be extracted.
+     */
+    public static ASN1Encodable extractParameters(AlgorithmParameters params)
+        throws IOException
+    {
+        // we try ASN.1 explicitly first just in case and then role back to the default.
+        ASN1Encodable asn1Params;
+        try
+        {
+            asn1Params = ASN1Primitive.fromByteArray(params.getEncoded("ASN.1"));
+        }
+        catch (Exception ex)
+        {
+            asn1Params = ASN1Primitive.fromByteArray(params.getEncoded());
+        }
+
+        return asn1Params;
+    }
+
+    public static void loadParameters(AlgorithmParameters params, ASN1Encodable sParams)
+        throws IOException
+    {
+        // we try ASN.1 explicitly first just in case and then role back to the default.
+        try
+        {
+            params.init(sParams.toASN1Primitive().getEncoded(), "ASN.1");
+        }
+        catch (Exception ex)
+        {
+            params.init(sParams.toASN1Primitive().getEncoded());
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherInputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherInputStream.java
new file mode 100644
index 0000000..84291ba
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherInputStream.java
@@ -0,0 +1,217 @@
+package org.bouncycastle.jcajce.io;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+
+import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
+
+/**
+ * A CipherInputStream is composed of an InputStream and a cipher so that read() methods return data
+ * that are read in from the underlying InputStream but have been additionally processed by the
+ * Cipher. The cipher must be fully initialized before being used by a CipherInputStream.
+ * <p/>
+ * For example, if the Cipher is initialized for decryption, the CipherInputStream will attempt to
+ * read in data and decrypt them, before returning the decrypted data.
+ * <p/>
+ * This is a reimplementation of {@link javax.crypto.CipherInputStream} that is safe for use with
+ * AEAD block ciphers, and does not silently catch {@link BadPaddingException} and
+ * {@link IllegalBlockSizeException} errors. Any errors that occur during {@link Cipher#doFinal()
+ * finalisation} are rethrown wrapped in an {@link InvalidCipherTextIOException}.
+ */
+public class CipherInputStream
+    extends FilterInputStream
+{
+    private final Cipher cipher;
+    private final byte[] inputBuffer = new byte[512];
+    private boolean finalized = false;
+    private byte[] buf;
+    private int maxBuf;
+    private int bufOff;
+
+    /**
+     * Constructs a CipherInputStream from an InputStream and an initialised Cipher.
+     */
+    public CipherInputStream(InputStream input, Cipher cipher)
+    {
+        super(input);
+        this.cipher = cipher;
+    }
+
+    /**
+     * Read data from underlying stream and process with cipher until end of stream or some data is
+     * available after cipher processing.
+     *
+     * @return -1 to indicate end of stream, or the number of bytes (> 0) available.
+     */
+    private int nextChunk()
+        throws IOException
+    {
+        if (finalized)
+        {
+            return -1;
+        }
+
+        bufOff = 0;
+        maxBuf = 0;
+
+        // Keep reading until EOF or cipher processing produces data
+        while (maxBuf == 0)
+        {
+            int read = in.read(inputBuffer);
+            if (read == -1)
+            {
+                buf = finaliseCipher();
+                if ((buf == null) || (buf.length == 0))
+                {
+                    return -1;
+                }
+                maxBuf = buf.length;
+                return maxBuf;
+            }
+
+            buf = cipher.update(inputBuffer, 0, read);
+            if (buf != null)
+            {
+                maxBuf = buf.length;
+            }
+        }
+        return maxBuf;
+    }
+
+    private byte[] finaliseCipher()
+        throws InvalidCipherTextIOException
+    {
+        try
+        {
+            finalized = true;
+            return cipher.doFinal();
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new InvalidCipherTextIOException("Error finalising cipher", e);
+        }
+    }
+
+    /**
+     * Reads data from the underlying stream and processes it with the cipher until the cipher
+     * outputs data, and returns the next available byte.
+     * <p/>
+     * If the underlying stream is exhausted by this call, the cipher will be finalised.
+     *
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
+    public int read()
+        throws IOException
+    {
+        if (bufOff >= maxBuf)
+        {
+            if (nextChunk() < 0)
+            {
+                return -1;
+            }
+        }
+
+        return buf[bufOff++] & 0xff;
+    }
+
+    /**
+     * Reads data from the underlying stream and processes it with the cipher until the cipher
+     * outputs data, and then returns up to <code>len</code> bytes in the provided array.
+     * <p/>
+     * If the underlying stream is exhausted by this call, the cipher will be finalised.
+     *
+     * @param b   the buffer into which the data is read.
+     * @param off the start offset in the destination array <code>b</code>
+     * @param len the maximum number of bytes read.
+     * @return the total number of bytes read into the buffer, or <code>-1</code> if there is no
+     *         more data because the end of the stream has been reached.
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
+    public int read(byte[] b, int off, int len)
+        throws IOException
+    {
+        if (bufOff >= maxBuf)
+        {
+            if (nextChunk() < 0)
+            {
+                return -1;
+            }
+        }
+
+        int toSupply = Math.min(len, available());
+        System.arraycopy(buf, bufOff, b, off, toSupply);
+        bufOff += toSupply;
+        return toSupply;
+    }
+
+    public long skip(long n)
+        throws IOException
+    {
+        if (n <= 0)
+        {
+            return 0;
+        }
+
+        int skip = (int)Math.min(n, available());
+        bufOff += skip;
+        return skip;
+    }
+
+    public int available()
+        throws IOException
+    {
+        return maxBuf - bufOff;
+    }
+
+    /**
+     * Closes the underlying input stream, and then finalises the processing of the data by the
+     * cipher.
+     *
+     * @throws IOException if there was an error closing the input stream.
+     * @throws InvalidCipherTextIOException if the data read from the stream was invalid ciphertext
+     * (e.g. the cipher is an AEAD cipher and the ciphertext tag check fails).
+     */
+    public void close()
+        throws IOException
+    {
+        try
+        {
+            in.close();
+        }
+        finally
+        {
+            if (!finalized)
+            {
+                // Reset the cipher, discarding any data buffered in it
+                // Errors in cipher finalisation trump I/O error closing input
+                finaliseCipher();
+            }
+        }
+        maxBuf = bufOff = 0;
+    }
+
+    public void mark(int readlimit)
+    {
+    }
+
+    public void reset()
+        throws IOException
+    {
+    }
+
+    public boolean markSupported()
+    {
+        return false;
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherOutputStream.java b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherOutputStream.java
new file mode 100644
index 0000000..814b339
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/io/CipherOutputStream.java
@@ -0,0 +1,147 @@
+package org.bouncycastle.jcajce.io;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.security.GeneralSecurityException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+
+import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
+
+/**
+ * A CipherOutputStream is composed of an OutputStream and a cipher so that write() methods process
+ * the written data with the cipher, and the output of the cipher is in turn written to the
+ * underlying OutputStream. The cipher must be fully initialized before being used by a
+ * CipherInputStream.
+ * <p/>
+ * For example, if the cipher is initialized for encryption, the CipherOutputStream will encrypt the
+ * data before writing the encrypted data to the underlying stream.
+ * <p/>
+ * This is a reimplementation of {@link javax.crypto.CipherOutputStream} that is safe for use with
+ * AEAD block ciphers, and does not silently catch {@link BadPaddingException} and
+ * {@link IllegalBlockSizeException} errors. Any errors that occur during {@link Cipher#doFinal()
+ * finalisation} are rethrown wrapped in an {@link InvalidCipherTextIOException}.
+ */
+public class CipherOutputStream
+    extends FilterOutputStream
+{
+    private final Cipher cipher;
+    private final byte[] oneByte = new byte[1];
+
+    /**
+     * Constructs a CipherOutputStream from an OutputStream and a Cipher.
+     */
+    public CipherOutputStream(OutputStream output, Cipher cipher)
+    {
+        super(output);
+        this.cipher = cipher;
+    }
+
+    /**
+     * Writes the specified byte to this output stream.
+     *
+     * @param b the <code>byte</code>.
+     * @throws java.io.IOException if an I/O error occurs.
+     */
+    public void write(int b)
+        throws IOException
+    {
+        oneByte[0] = (byte)b;
+        write(oneByte, 0, 1);
+    }
+
+    /**
+     * Writes <code>len</code> bytes from the specified byte array starting at offset
+     * <code>off</code> to this output stream.
+     *
+     * @param b   the data.
+     * @param off the start offset in the data.
+     * @param len the number of bytes to write.
+     * @throws java.io.IOException if an I/O error occurs.
+     */
+    public void write(byte[] b, int off, int len)
+        throws IOException
+    {
+        byte[] outData = cipher.update(b, off, len);
+        if (outData != null)
+        {
+            out.write(outData);
+        }
+    }
+
+    /**
+     * Flushes this output stream by forcing any buffered output bytes that have already been
+     * processed by the encapsulated cipher object to be written out.
+     * <p/>
+     * <p/>
+     * Any bytes buffered by the encapsulated cipher and waiting to be processed by it will not be
+     * written out. For example, if the encapsulated cipher is a block cipher, and the total number
+     * of bytes written using one of the <code>write</code> methods is less than the cipher's block
+     * size, no bytes will be written out.
+     *
+     * @throws java.io.IOException if an I/O error occurs.
+     */
+    public void flush()
+        throws IOException
+    {
+        out.flush();
+    }
+
+    /**
+     * Closes this output stream and releases any system resources associated with this stream.
+     * <p/>
+     * This method invokes the <code>doFinal</code> method of the encapsulated cipher object, which
+     * causes any bytes buffered by the encapsulated cipher to be processed. The result is written
+     * out by calling the <code>flush</code> method of this output stream.
+     * <p/>
+     * This method resets the encapsulated cipher object to its initial state and calls the
+     * <code>close</code> method of the underlying output stream.
+     *
+     * @throws java.io.IOException if an I/O error occurs.
+     * @throws InvalidCipherTextIOException if the data written to this stream was invalid
+     * ciphertext (e.g. the cipher is an AEAD cipher and the ciphertext tag check
+     * fails).
+     */
+    public void close()
+        throws IOException
+    {
+        IOException error = null;
+        try
+        {
+            byte[] outData = cipher.doFinal();
+            if (outData != null)
+            {
+                out.write(outData);
+            }
+        }
+        catch (GeneralSecurityException e)
+        {
+            error = new InvalidCipherTextIOException("Error during cipher finalisation", e);
+        }
+        catch (Exception e)
+        {
+            error = new IOException("Error closing stream: " + e);
+        }
+        try
+        {
+            flush();
+            out.close();
+        }
+        catch (IOException e)
+        {
+            // Invalid ciphertext takes precedence over close error
+            if (error == null)
+            {
+                error = e;
+            }
+        }
+        if (error != null)
+        {
+            throw error;
+        }
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
index 3e16254..2efffbf 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/DSA.java
@@ -32,6 +32,13 @@
 
             provider.addAlgorithm("Alg.Alias.Signature.RAWDSA", "NONEWITHDSA");
 
+            provider.addAlgorithm("Signature.DETDSA", PREFIX + "DSASigner$detDSA");
+            provider.addAlgorithm("Signature.SHA1WITHDETDSA", PREFIX + "DSASigner$detDSA");
+            provider.addAlgorithm("Signature.SHA224WITHDETDSA", PREFIX + "DSASigner$detDSA224");
+            provider.addAlgorithm("Signature.SHA256WITHDETDSA", PREFIX + "DSASigner$detDSA256");
+            provider.addAlgorithm("Signature.SHA384WITHDETDSA", PREFIX + "DSASigner$detDSA384");
+            provider.addAlgorithm("Signature.SHA512WITHDETDSA", PREFIX + "DSASigner$detDSA512");
+
             addSignatureAlgorithm(provider, "SHA224", "DSA", PREFIX + "DSASigner$dsa224", NISTObjectIdentifiers.dsa_with_sha224);
             addSignatureAlgorithm(provider, "SHA256", "DSA", PREFIX + "DSASigner$dsa256", NISTObjectIdentifiers.dsa_with_sha256);
             addSignatureAlgorithm(provider, "SHA384", "DSA", PREFIX + "DSASigner$dsa384", NISTObjectIdentifiers.dsa_with_sha384);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
index 4c2ca28..d06e05c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/EC.java
@@ -67,6 +67,13 @@
             provider.addAlgorithm("Alg.Alias.Signature.1.2.840.10045.4.1", "ECDSA");
             provider.addAlgorithm("Alg.Alias.Signature." + TeleTrusTObjectIdentifiers.ecSignWithSha1, "ECDSA");
 
+            provider.addAlgorithm("Signature.DETECDSA", PREFIX + "SignatureSpi$ecDetDSA");
+            provider.addAlgorithm("Signature.SHA1WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA");
+            provider.addAlgorithm("Signature.SHA224WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA224");
+            provider.addAlgorithm("Signature.SHA256WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA256");
+            provider.addAlgorithm("Signature.SHA384WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA384");
+            provider.addAlgorithm("Signature.SHA512WITHDETECDSA", PREFIX + "SignatureSpi$ecDetDSA512");
+
             addSignatureAlgorithm(provider, "SHA224", "ECDSA", PREFIX + "SignatureSpi$ecDSA224", X9ObjectIdentifiers.ecdsa_with_SHA224);
             addSignatureAlgorithm(provider, "SHA256", "ECDSA", PREFIX + "SignatureSpi$ecDSA256", X9ObjectIdentifiers.ecdsa_with_SHA256);
             addSignatureAlgorithm(provider, "SHA384", "ECDSA", PREFIX + "SignatureSpi$ecDSA384", X9ObjectIdentifiers.ecdsa_with_SHA384);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
index c9462a6..f2b5314 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dh/KeyAgreementSpi.java
@@ -50,17 +50,34 @@
     private byte[] bigIntToBytes(
         BigInteger    r)
     {
+        //
+        // RFC 2631 (2.1.2) specifies that the secret should be padded with leading zeros if necessary
+        // must be the same length as p
+        //
+        int expectedLength = (p.bitLength() + 7) / 8;
+
         byte[]    tmp = r.toByteArray();
-        
-        if (tmp[0] == 0)
+
+        if (tmp.length == expectedLength)
         {
-            byte[]    ntmp = new byte[tmp.length - 1];
-            
-            System.arraycopy(tmp, 1, ntmp, 0, ntmp.length);
-            return ntmp;
+            return tmp;
         }
-        
-        return tmp;
+
+        if (tmp[0] == 0 && tmp.length == expectedLength + 1)
+        {
+            byte[]    rv = new byte[tmp.length - 1];
+            
+            System.arraycopy(tmp, 1, rv, 0, rv.length);
+            return rv;
+        }
+
+        // tmp must be shorter than expectedLength
+        // pad to the left with zeros.
+        byte[]    rv = new byte[expectedLength];
+
+        System.arraycopy(tmp, 0, rv, rv.length - tmp.length, tmp.length);
+
+        return rv;
     }
     
     protected Key engineDoPhase(
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
index ef12b4f..ade49b3 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dsa/DSASigner.java
@@ -29,6 +29,7 @@
 import org.bouncycastle.crypto.digests.SHA384Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
 
 public class DSASigner
     extends SignatureSpi
@@ -220,6 +221,15 @@
         }
     }
 
+    static public class detDSA
+        extends DSASigner
+    {
+        public detDSA()
+        {
+            super(new SHA1Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA1Digest())));
+        }
+    }
+
     static public class dsa224
         extends DSASigner
     {
@@ -228,7 +238,16 @@
             super(new SHA224Digest(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-    
+
+    static public class detDSA224
+        extends DSASigner
+    {
+        public detDSA224()
+        {
+            super(new SHA224Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA224Digest())));
+        }
+    }
+
     static public class dsa256
         extends DSASigner
     {
@@ -237,7 +256,16 @@
             super(new SHA256Digest(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-    
+
+    static public class detDSA256
+        extends DSASigner
+    {
+        public detDSA256()
+        {
+            super(new SHA256Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA256Digest())));
+        }
+    }
+
     static public class dsa384
         extends DSASigner
     {
@@ -246,7 +274,16 @@
             super(new SHA384Digest(), new org.bouncycastle.crypto.signers.DSASigner());
         }
     }
-    
+
+    static public class detDSA384
+        extends DSASigner
+    {
+        public detDSA384()
+        {
+            super(new SHA384Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA384Digest())));
+        }
+    }
+
     static public class dsa512
         extends DSASigner
     {
@@ -256,6 +293,15 @@
         }
     }
 
+    static public class detDSA512
+        extends DSASigner
+    {
+        public detDSA512()
+        {
+            super(new SHA512Digest(), new org.bouncycastle.crypto.signers.DSASigner(new HMacDSAKCalculator(new SHA512Digest())));
+        }
+    }
+
     static public class noneDSA
         extends DSASigner
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java
index 56fe741..9b7e797 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PrivateKey.java
@@ -119,8 +119,8 @@
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                    dp.getG().getX().toBigInteger(),
-                    dp.getG().getY().toBigInteger()),
+                    dp.getG().getAffineXCoord().toBigInteger(),
+                    dp.getG().getAffineYCoord().toBigInteger()),
                 dp.getN(),
                 dp.getH().intValue());
         }
@@ -150,8 +150,8 @@
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                    dp.getG().getX().toBigInteger(),
-                    dp.getG().getY().toBigInteger()),
+                    dp.getG().getAffineXCoord().toBigInteger(),
+                    dp.getG().getAffineYCoord().toBigInteger()),
                 dp.getN(),
                 dp.getH().intValue());
         }
@@ -162,8 +162,8 @@
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                    spec.getG().getX().toBigInteger(),
-                    spec.getG().getY().toBigInteger()),
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
                 spec.getN(),
                 spec.getH().intValue());
         }
@@ -206,8 +206,8 @@
                     oid.getId(),
                     ellipticCurve,
                     new ECPoint(
-                        gParam.getG().getX().toBigInteger(),
-                        gParam.getG().getY().toBigInteger()),
+                        gParam.getG().getAffineXCoord().toBigInteger(),
+                        gParam.getG().getAffineYCoord().toBigInteger()),
                     gParam.getN(),
                     gParam.getH());
             }
@@ -219,8 +219,8 @@
                     ECUtil.getCurveName(oid),
                     ellipticCurve,
                     new ECPoint(
-                        ecP.getG().getX().toBigInteger(),
-                        ecP.getG().getY().toBigInteger()),
+                        ecP.getG().getAffineXCoord().toBigInteger(),
+                        ecP.getG().getAffineYCoord().toBigInteger()),
                     ecP.getN(),
                     ecP.getH());
             }
@@ -237,8 +237,8 @@
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                    ecP.getG().getX().toBigInteger(),
-                    ecP.getG().getY().toBigInteger()),
+                    ecP.getG().getAffineXCoord().toBigInteger(),
+                    ecP.getG().getAffineYCoord().toBigInteger()),
                 ecP.getN(),
                 ecP.getH().intValue());
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java
index a060ae6..c641ee9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/dstu/BCDSTU4145PublicKey.java
@@ -16,7 +16,6 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.ua.DSTU4145BinaryField;
 import org.bouncycastle.asn1.ua.DSTU4145ECBinary;
@@ -28,13 +27,9 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
-import org.bouncycastle.asn1.x9.X9ECPoint;
-import org.bouncycastle.asn1.x9.X9IntegerConverter;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
-import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -88,7 +83,7 @@
             {
                 org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-                q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false);
+                q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
             }
             this.ecSpec = null;
         }
@@ -157,8 +152,8 @@
         return new ECParameterSpec(
             ellipticCurve,
             new ECPoint(
-                dp.getG().getX().toBigInteger(),
-                dp.getG().getY().toBigInteger()),
+                dp.getG().getAffineXCoord().toBigInteger(),
+                dp.getG().getAffineYCoord().toBigInteger()),
             dp.getN(),
             dp.getH().intValue());
     }
@@ -191,155 +186,79 @@
 
     private void populateFromPubKeyInfo(SubjectPublicKeyInfo info)
     {
-        if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145be) || info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+        DERBitString bits = info.getPublicKeyData();
+        ASN1OctetString key;
+        this.algorithm = "DSTU4145";
+
+        try
         {
-            DERBitString bits = info.getPublicKeyData();
-            ASN1OctetString key;
-            this.algorithm = "DSTU4145";
+            key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
+        }
+        catch (IOException ex)
+        {
+            throw new IllegalArgumentException("error recovering public key");
+        }
 
-            try
-            {
-                key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
-            }
-            catch (IOException ex)
-            {
-                throw new IllegalArgumentException("error recovering public key");
-            }
+        byte[] keyEnc = key.getOctets();
 
-            byte[] keyEnc = key.getOctets();
+        if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
+        {
+            reverseBytes(keyEnc);
+        }
 
-            if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
-            {
-                reverseBytes(keyEnc);
-            }
+        dstuParams = DSTU4145Params.getInstance((ASN1Sequence)info.getAlgorithm().getParameters());
 
-            dstuParams = DSTU4145Params.getInstance((ASN1Sequence)info.getAlgorithm().getParameters());
+        //ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+        org.bouncycastle.jce.spec.ECParameterSpec spec = null;
+        if (dstuParams.isNamedCurve())
+        {
+            ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve();
+            ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid);
 
-            //ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
-            org.bouncycastle.jce.spec.ECParameterSpec spec = null;
-            if (dstuParams.isNamedCurve())
-            {
-                ASN1ObjectIdentifier curveOid = dstuParams.getNamedCurve();
-                ECDomainParameters ecP = DSTU4145NamedCurves.getByOID(curveOid);
-
-                spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
-            }
-            else
-            {
-                DSTU4145ECBinary binary = dstuParams.getECBinary();
-                byte[] b_bytes = binary.getB();
-                if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
-                {
-                    reverseBytes(b_bytes);
-                }
-                DSTU4145BinaryField field = binary.getField();
-                ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes));
-                byte[] g_bytes = binary.getG();
-                if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
-                {
-                    reverseBytes(g_bytes);
-                }
-                spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN());
-            }
-
-            ECCurve curve = spec.getCurve();
-            EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
-
-            //this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
-            this.q = DSTU4145PointEncoder.decodePoint(curve, keyEnc);
-
-            if (dstuParams.isNamedCurve())
-            {
-                ecSpec = new ECNamedCurveSpec(
-                    dstuParams.getNamedCurve().getId(),
-                    ellipticCurve,
-                    new ECPoint(
-                        spec.getG().getX().toBigInteger(),
-                        spec.getG().getY().toBigInteger()),
-                    spec.getN(), spec.getH());
-            }
-            else
-            {
-                ecSpec = new ECParameterSpec(
-                    ellipticCurve,
-                    new ECPoint(
-                        spec.getG().getX().toBigInteger(),
-                        spec.getG().getY().toBigInteger()),
-                    spec.getN(), spec.getH().intValue());
-            }
-
+            spec = new ECNamedCurveParameterSpec(curveOid.getId(), ecP.getCurve(), ecP.getG(), ecP.getN(), ecP.getH(), ecP.getSeed());
         }
         else
         {
-            X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters());
-            ECCurve curve;
-            EllipticCurve ellipticCurve;
-
-            if (params.isNamedCurve())
+            DSTU4145ECBinary binary = dstuParams.getECBinary();
+            byte[] b_bytes = binary.getB();
+            if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
             {
-                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
-                X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
-
-                curve = ecP.getCurve();
-                ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
-
-                ecSpec = new ECNamedCurveSpec(
-                    ECUtil.getCurveName(oid),
-                    ellipticCurve,
-                    new ECPoint(
-                        ecP.getG().getX().toBigInteger(),
-                        ecP.getG().getY().toBigInteger()),
-                    ecP.getN(),
-                    ecP.getH());
+                reverseBytes(b_bytes);
             }
-            else if (params.isImplicitlyCA())
+            DSTU4145BinaryField field = binary.getField();
+            ECCurve curve = new ECCurve.F2m(field.getM(), field.getK1(), field.getK2(), field.getK3(), binary.getA(), new BigInteger(1, b_bytes));
+            byte[] g_bytes = binary.getG();
+            if (info.getAlgorithm().getAlgorithm().equals(UAObjectIdentifiers.dstu4145le))
             {
-                ecSpec = null;
-                curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve();
+                reverseBytes(g_bytes);
             }
-            else
-            {
-                X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+            spec = new org.bouncycastle.jce.spec.ECParameterSpec(curve, DSTU4145PointEncoder.decodePoint(curve, g_bytes), binary.getN());
+        }
 
-                curve = ecP.getCurve();
-                ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
+        ECCurve curve = spec.getCurve();
+        EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
 
-                this.ecSpec = new ECParameterSpec(
-                    ellipticCurve,
-                    new ECPoint(
-                        ecP.getG().getX().toBigInteger(),
-                        ecP.getG().getY().toBigInteger()),
-                    ecP.getN(),
-                    ecP.getH().intValue());
-            }
+        //this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
+        this.q = DSTU4145PointEncoder.decodePoint(curve, keyEnc);
 
-            DERBitString bits = info.getPublicKeyData();
-            byte[] data = bits.getBytes();
-            ASN1OctetString key = new DEROctetString(data);
-
-            //
-            // extra octet string - one of our old certs...
-            //
-            if (data[0] == 0x04 && data[1] == data.length - 2
-                && (data[2] == 0x02 || data[2] == 0x03))
-            {
-                int qLength = new X9IntegerConverter().getByteLength(curve);
-
-                if (qLength >= data.length - 3)
-                {
-                    try
-                    {
-                        key = (ASN1OctetString)ASN1Primitive.fromByteArray(data);
-                    }
-                    catch (IOException ex)
-                    {
-                        throw new IllegalArgumentException("error recovering public key");
-                    }
-                }
-            }
-            X9ECPoint derQ = new X9ECPoint(curve, key);
-
-            this.q = derQ.getPoint();
+        if (dstuParams.isNamedCurve())
+        {
+            ecSpec = new ECNamedCurveSpec(
+                dstuParams.getNamedCurve().getId(),
+                ellipticCurve,
+                new ECPoint(
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
+                spec.getN(), spec.getH());
+        }
+        else
+        {
+            ecSpec = new ECParameterSpec(
+                ellipticCurve,
+                new ECPoint(
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
+                spec.getN(), spec.getH().intValue());
         }
     }
 
@@ -370,61 +289,18 @@
         ASN1Encodable params;
         SubjectPublicKeyInfo info;
 
-        if (algorithm.equals("DSTU4145"))
+        if (dstuParams != null)
         {
-            if (dstuParams != null)
-            {
-                params = dstuParams;
-            }
-            else
-            {
-                if (ecSpec instanceof ECNamedCurveSpec)
-                {
-                    params = new DSTU4145Params(new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()));
-                }
-                else
-                {   // strictly speaking this may not be applicable...
-                    ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
-
-                    X9ECParameters ecP = new X9ECParameters(
-                        curve,
-                        EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
-                        ecSpec.getOrder(),
-                        BigInteger.valueOf(ecSpec.getCofactor()),
-                        ecSpec.getCurve().getSeed());
-
-                    params = new X962Parameters(ecP);
-                }
-            }
-
-            byte[] encKey = DSTU4145PointEncoder.encodePoint(this.q);
-
-            try
-            {
-                info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(UAObjectIdentifiers.dstu4145be, params), new DEROctetString(encKey));
-            }
-            catch (IOException e)
-            {
-                return null;
-            }
+            params = dstuParams;
         }
         else
         {
             if (ecSpec instanceof ECNamedCurveSpec)
             {
-                ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
-                if (curveOid == null)
-                {
-                    curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
-                }
-                params = new X962Parameters(curveOid);
-            }
-            else if (ecSpec == null)
-            {
-                params = new X962Parameters(DERNull.INSTANCE);
+                params = new DSTU4145Params(new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName()));
             }
             else
-            {
+            {   // strictly speaking this may not be applicable...
                 ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
 
                 X9ECParameters ecP = new X9ECParameters(
@@ -436,12 +312,17 @@
 
                 params = new X962Parameters(ecP);
             }
+        }
 
-            ECCurve curve = this.engineGetQ().getCurve();
-            ASN1OctetString p = (ASN1OctetString)
-                new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
+        byte[] encKey = DSTU4145PointEncoder.encodePoint(this.q);
 
-            info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
+        try
+        {
+            info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(UAObjectIdentifiers.dstu4145be, params), new DEROctetString(encKey));
+        }
+        catch (IOException e)
+        {
+            return null;
         }
 
         return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
@@ -464,7 +345,7 @@
 
     public ECPoint getW()
     {
-        return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
+        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -473,11 +354,11 @@
         {
             if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
             {
-                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
             else
             {
-                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
         }
 
@@ -505,8 +386,8 @@
         String nl = System.getProperty("line.separator");
 
         buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
+        buf.append("            X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
+        buf.append("            Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
 
         return buf.toString();
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
index ac04d3c..45d5b08 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPrivateKey.java
@@ -13,14 +13,11 @@
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERNull;
-import org.bouncycastle.asn1.DERObjectIdentifier;
-import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
-import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -138,8 +135,8 @@
             this.ecSpec = new ECParameterSpec(
                             ellipticCurve,
                             new ECPoint(
-                                    dp.getG().getX().toBigInteger(),
-                                    dp.getG().getY().toBigInteger()),
+                                    dp.getG().getAffineXCoord().toBigInteger(),
+                                    dp.getG().getAffineYCoord().toBigInteger()),
                             dp.getN(),
                             dp.getH().intValue());
         }
@@ -171,22 +168,16 @@
             this.ecSpec = new ECParameterSpec(
                             ellipticCurve,
                             new ECPoint(
-                                    dp.getG().getX().toBigInteger(),
-                                    dp.getG().getY().toBigInteger()),
+                                    dp.getG().getAffineXCoord().toBigInteger(),
+                                    dp.getG().getAffineYCoord().toBigInteger()),
                             dp.getN(),
                             dp.getH().intValue());
         }
         else
         {
             EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
-            
-            this.ecSpec = new ECParameterSpec(
-                                ellipticCurve,
-                                new ECPoint(
-                                        spec.getG().getX().toBigInteger(),
-                                        spec.getG().getY().toBigInteger()),
-                                spec.getN(),
-                                spec.getH().intValue());
+
+            this.ecSpec = EC5Util.convertSpec(ellipticCurve, spec);
         }
 
         publicKey = getPublicKeyDetails(pubKey);
@@ -223,34 +214,16 @@
         {
             ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
             X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
-            if (ecP == null) // GOST Curve
-            {
-                ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
-                EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
-
-                ecSpec = new ECNamedCurveSpec(
-                        ECGOST3410NamedCurves.getName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                                gParam.getG().getX().toBigInteger(),
-                                gParam.getG().getY().toBigInteger()),
-                        gParam.getN(),
-                        gParam.getH());
-            }
-            else
-            {
-                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
-
-                ecSpec = new ECNamedCurveSpec(
-                        ECUtil.getCurveName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH());
-            }
+            ecSpec = new ECNamedCurveSpec(
+                    ECUtil.getCurveName(oid),
+                    ellipticCurve,
+                    new ECPoint(
+                            ecP.getG().getAffineXCoord().toBigInteger(),
+                            ecP.getG().getAffineYCoord().toBigInteger()),
+                    ecP.getN(),
+                    ecP.getH());
         }
         else if (params.isImplicitlyCA())
         {
@@ -264,16 +237,16 @@
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                        ecP.getG().getX().toBigInteger(),
-                        ecP.getG().getY().toBigInteger()),
+                        ecP.getG().getAffineXCoord().toBigInteger(),
+                        ecP.getG().getAffineYCoord().toBigInteger()),
                 ecP.getN(),
                 ecP.getH().intValue());
         }
 
         ASN1Encodable privKey = info.parsePrivateKey();
-        if (privKey instanceof DERInteger)
+        if (privKey instanceof ASN1Integer)
         {
-            DERInteger          derD = DERInteger.getInstance(privKey);
+            ASN1Integer          derD = ASN1Integer.getInstance(privKey);
 
             this.d = derD.getValue();
         }
@@ -313,11 +286,12 @@
 
         if (ecSpec instanceof ECNamedCurveSpec)
         {
-            DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
+            ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
             if (curveOid == null)  // guess it's the OID
             {
-                curveOid = new DERObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+                curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
             }
+
             params = new X962Parameters(curveOid);
         }
         else if (ecSpec == null)
@@ -352,15 +326,7 @@
 
         try
         {
-            if (algorithm.equals("ECGOST3410"))
-            {
-                info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive());
-            }
-            else
-            {
-
-                info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive());
-            }
+            info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), keyStructure);
 
             return info.getEncoded(ASN1Encoding.DER);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
index 2b61727..0eaae1d 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/BCECPublicKey.java
@@ -90,7 +90,7 @@
             {
                 org.bouncycastle.jce.spec.ECParameterSpec s = configuration.getEcImplicitlyCa();
 
-                q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false);
+                q = s.getCurve().createPoint(q.getXCoord().toBigInteger(), q.getYCoord().toBigInteger(), false);
             }               
             this.ecSpec = null;
         }
@@ -188,8 +188,8 @@
         return new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                        dp.getG().getX().toBigInteger(),
-                        dp.getG().getY().toBigInteger()),
+                        dp.getG().getAffineXCoord().toBigInteger(),
+                        dp.getG().getAffineYCoord().toBigInteger()),
                         dp.getN(),
                         dp.getH().intValue());
     }
@@ -212,8 +212,8 @@
                     ECUtil.getCurveName(oid),
                     ellipticCurve,
                     new ECPoint(
-                            ecP.getG().getX().toBigInteger(),
-                            ecP.getG().getY().toBigInteger()),
+                            ecP.getG().getAffineXCoord().toBigInteger(),
+                            ecP.getG().getAffineYCoord().toBigInteger()),
                     ecP.getN(),
                     ecP.getH());
         }
@@ -232,8 +232,8 @@
             this.ecSpec = new ECParameterSpec(
                     ellipticCurve,
                     new ECPoint(
-                            ecP.getG().getX().toBigInteger(),
-                            ecP.getG().getY().toBigInteger()),
+                            ecP.getG().getAffineXCoord().toBigInteger(),
+                            ecP.getG().getAffineYCoord().toBigInteger()),
                     ecP.getN(),
                     ecP.getH().intValue());
         }
@@ -310,8 +310,19 @@
         }
 
         ECCurve curve = this.engineGetQ().getCurve();
-        ASN1OctetString p = (ASN1OctetString)
-            new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
+        ASN1OctetString p;
+
+        // stored curve is null if ImplicitlyCa
+        if (ecSpec == null)
+        {
+            p = (ASN1OctetString)
+                new X9ECPoint(curve.createPoint(this.getQ().getXCoord().toBigInteger(), this.getQ().getYCoord().toBigInteger(), withCompression)).toASN1Primitive();
+        }
+        else
+        {
+            p = (ASN1OctetString)
+                            new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive();
+        }
 
         info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
 
@@ -351,7 +362,7 @@
 
     public ECPoint getW()
     {
-        return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
+        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -360,11 +371,11 @@
         {
             if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
             {
-                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
             else
             {
-                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
         }
 
@@ -392,8 +403,8 @@
         String          nl = System.getProperty("line.separator");
 
         buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
+        buf.append("            X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
+        buf.append("            Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
 
         return buf.toString();
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
index c609d95..0556378 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyAgreementSpi.java
@@ -15,7 +15,7 @@
 import javax.crypto.ShortBufferException;
 import javax.crypto.spec.SecretKeySpec;
 
-import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9IntegerConverter;
@@ -76,7 +76,7 @@
     private byte[] bigIntToBytes(
         BigInteger    r)
     {
-        return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getX()));
+        return converter.integerToBytes(r, converter.getByteLength(parameters.getG().getAffineXCoord()));
     }
 
     protected KeyAgreementSpi(
@@ -185,7 +185,7 @@
             
             int    keySize = ((Integer)algorithms.get(algorithm)).intValue();
 
-            DHKDFParameters params = new DHKDFParameters(new DERObjectIdentifier(algorithm), keySize, secret);
+            DHKDFParameters params = new DHKDFParameters(new ASN1ObjectIdentifier(algorithm), keySize, secret);
 
             byte[] keyBytes = new byte[keySize / 8];
             kdf.init(params);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
index 5e1a8a3..f47f8a2 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/KeyPairGeneratorSpi.java
@@ -10,10 +10,7 @@
 import java.util.Hashtable;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
 import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
@@ -148,46 +145,22 @@
                     curveName = ((ECNamedCurveGenParameterSpec)params).getName();
                 }
 
-                X9ECParameters  ecP = X962NamedCurves.getByName(curveName);
+                X9ECParameters  ecP = ECNamedCurveTable.getByName(curveName);
                 if (ecP == null)
                 {
-                    ecP = SECNamedCurves.getByName(curveName);
-                    if (ecP == null)
+                    // See if it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug)
+                    try
                     {
-                        ecP = NISTNamedCurves.getByName(curveName);
-                    }
-                    if (ecP == null)
-                    {
-                        ecP = TeleTrusTNamedCurves.getByName(curveName);
-                    }
-                    if (ecP == null)
-                    {
-                        // See if it's actually an OID string (SunJSSE ServerHandshaker setupEphemeralECDHKeys bug)
-                        try
+                        ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(curveName);
+                        ecP = ECNamedCurveTable.getByOID(oid);
+                        if (ecP == null)
                         {
-                            ASN1ObjectIdentifier oid = new ASN1ObjectIdentifier(curveName);
-                            ecP = X962NamedCurves.getByOID(oid);
-                            if (ecP == null)
-                            {
-                                ecP = SECNamedCurves.getByOID(oid);
-                            }
-                            if (ecP == null)
-                            {
-                                ecP = NISTNamedCurves.getByOID(oid);
-                            }
-                            if (ecP == null)
-                            {
-                                ecP = TeleTrusTNamedCurves.getByOID(oid);
-                            }
-                            if (ecP == null)
-                            {
-                                throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName);
-                            }
+                            throw new InvalidAlgorithmParameterException("unknown curve OID: " + curveName);
                         }
-                        catch (IllegalArgumentException ex)
-                        {
-                            throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
-                        }
+                    }
+                    catch (IllegalArgumentException ex)
+                    {
+                        throw new InvalidAlgorithmParameterException("unknown curve name: " + curveName);
                     }
                 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
index 29c50f4..e94746c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ec/SignatureSpi.java
@@ -25,6 +25,7 @@
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.signers.ECDSASigner;
 import org.bouncycastle.crypto.signers.ECNRSigner;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
 import org.bouncycastle.jcajce.provider.asymmetric.util.DSABase;
 import org.bouncycastle.jcajce.provider.asymmetric.util.DSAEncoder;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
@@ -73,6 +74,15 @@
         }
     }
 
+    static public class ecDetDSA
+        extends SignatureSpi
+    {
+        public ecDetDSA()
+        {
+            super(new SHA1Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA1Digest())), new StdDSAEncoder());
+        }
+    }
+
     static public class ecDSAnone
         extends SignatureSpi
     {
@@ -91,6 +101,15 @@
         }
     }
 
+    static public class ecDetDSA224
+        extends SignatureSpi
+    {
+        public ecDetDSA224()
+        {
+            super(new SHA224Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA224Digest())), new StdDSAEncoder());
+        }
+    }
+
     static public class ecDSA256
         extends SignatureSpi
     {
@@ -100,6 +119,15 @@
         }
     }
 
+    static public class ecDetDSA256
+        extends SignatureSpi
+    {
+        public ecDetDSA256()
+        {
+            super(new SHA256Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA256Digest())), new StdDSAEncoder());
+        }
+    }
+
     static public class ecDSA384
         extends SignatureSpi
     {
@@ -109,6 +137,15 @@
         }
     }
 
+    static public class ecDetDSA384
+        extends SignatureSpi
+    {
+        public ecDetDSA384()
+        {
+            super(new SHA384Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA384Digest())), new StdDSAEncoder());
+        }
+    }
+
     static public class ecDSA512
         extends SignatureSpi
     {
@@ -118,6 +155,15 @@
         }
     }
 
+    static public class ecDetDSA512
+        extends SignatureSpi
+    {
+        public ecDetDSA512()
+        {
+            super(new SHA512Digest(), new ECDSASigner(new HMacDSAKCalculator(new SHA512Digest())), new StdDSAEncoder());
+        }
+    }
+
     static public class ecDSARipeMD160
         extends SignatureSpi
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java
index 88d81c0..2b1c3fa 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PrivateKey.java
@@ -14,27 +14,32 @@
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.DERInteger;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERObjectIdentifier;
+import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
+import org.bouncycastle.asn1.cryptopro.GOST3410PublicKeyAlgParameters;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
 import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.PKCS12BagAttributeCarrierImpl;
+import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 import org.bouncycastle.jce.spec.ECNamedCurveSpec;
 import org.bouncycastle.math.ec.ECCurve;
 
@@ -43,10 +48,11 @@
 {
     static final long serialVersionUID = 7245981689601667138L;
 
-    private String          algorithm = "ECGOST3410";
-    private boolean         withCompression;
+    private String algorithm = "ECGOST3410";
+    private boolean withCompression;
 
-    private transient BigInteger      d;
+    private transient GOST3410PublicKeyAlgParameters gostParams;
+    private transient BigInteger d;
     private transient ECParameterSpec ecSpec;
     private transient DERBitString publicKey;
     private transient PKCS12BagAttributeCarrierImpl attrCarrier = new PKCS12BagAttributeCarrierImpl();
@@ -99,6 +105,7 @@
         this.withCompression = key.withCompression;
         this.attrCarrier = key.attrCarrier;
         this.publicKey = key.publicKey;
+        this.gostParams = key.gostParams;
     }
 
     public BCECGOST3410PrivateKey(
@@ -107,7 +114,7 @@
         BCECGOST3410PublicKey pubKey,
         ECParameterSpec spec)
     {
-        ECDomainParameters      dp = params.getParameters();
+        ECDomainParameters dp = params.getParameters();
 
         this.algorithm = algorithm;
         this.d = params.getD();
@@ -117,18 +124,20 @@
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
-                            ellipticCurve,
-                            new ECPoint(
-                                    dp.getG().getX().toBigInteger(),
-                                    dp.getG().getY().toBigInteger()),
-                            dp.getN(),
-                            dp.getH().intValue());
+                ellipticCurve,
+                new ECPoint(
+                    dp.getG().getAffineXCoord().toBigInteger(),
+                    dp.getG().getAffineYCoord().toBigInteger()),
+                dp.getN(),
+                dp.getH().intValue());
         }
         else
         {
             this.ecSpec = spec;
         }
 
+        this.gostParams = pubKey.getGostParams();
+
         publicKey = getPublicKeyDetails(pubKey);
     }
 
@@ -138,7 +147,7 @@
         BCECGOST3410PublicKey pubKey,
         org.bouncycastle.jce.spec.ECParameterSpec spec)
     {
-        ECDomainParameters      dp = params.getParameters();
+        ECDomainParameters dp = params.getParameters();
 
         this.algorithm = algorithm;
         this.d = params.getD();
@@ -148,26 +157,28 @@
             EllipticCurve ellipticCurve = EC5Util.convertCurve(dp.getCurve(), dp.getSeed());
 
             this.ecSpec = new ECParameterSpec(
-                            ellipticCurve,
-                            new ECPoint(
-                                    dp.getG().getX().toBigInteger(),
-                                    dp.getG().getY().toBigInteger()),
-                            dp.getN(),
-                            dp.getH().intValue());
+                ellipticCurve,
+                new ECPoint(
+                    dp.getG().getAffineXCoord().toBigInteger(),
+                    dp.getG().getAffineYCoord().toBigInteger()),
+                dp.getN(),
+                dp.getH().intValue());
         }
         else
         {
             EllipticCurve ellipticCurve = EC5Util.convertCurve(spec.getCurve(), spec.getSeed());
-            
+
             this.ecSpec = new ECParameterSpec(
-                                ellipticCurve,
-                                new ECPoint(
-                                        spec.getG().getX().toBigInteger(),
-                                        spec.getG().getY().toBigInteger()),
-                                spec.getN(),
-                                spec.getH().intValue());
+                ellipticCurve,
+                new ECPoint(
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
+                spec.getN(),
+                spec.getH().intValue());
         }
 
+        this.gostParams = pubKey.getGostParams();
+
         publicKey = getPublicKeyDetails(pubKey);
     }
 
@@ -190,72 +201,107 @@
     private void populateFromPrivKeyInfo(PrivateKeyInfo info)
         throws IOException
     {
-        X962Parameters params = new X962Parameters((ASN1Primitive)info.getPrivateKeyAlgorithm().getParameters());
+        ASN1Primitive p = info.getPrivateKeyAlgorithm().getParameters().toASN1Primitive();
 
-        if (params.isNamedCurve())
+        if (p instanceof ASN1Sequence && (ASN1Sequence.getInstance(p).size() == 2 || ASN1Sequence.getInstance(p).size() == 3))
         {
-            ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
-            X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
+            gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
 
-            if (ecP == null) // GOST Curve
+            ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+
+            ECCurve curve = spec.getCurve();
+            EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
+
+            ecSpec = new ECNamedCurveSpec(
+                ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+                ellipticCurve,
+                new ECPoint(
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
+                spec.getN(), spec.getH());
+
+            ASN1Encodable privKey = info.parsePrivateKey();
+
+            byte[] encVal = ASN1OctetString.getInstance(privKey).getOctets();
+            byte[] dVal = new byte[encVal.length];
+
+            for (int i = 0; i != encVal.length; i++)
             {
-                ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
-                EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
+                dVal[i] = encVal[encVal.length - 1 - i];
+            }
 
-                ecSpec = new ECNamedCurveSpec(
+            this.d = new BigInteger(1, dVal);
+        }
+        else
+        {
+            // for backwards compatibility
+            X962Parameters params = X962Parameters.getInstance(info.getPrivateKeyAlgorithm().getParameters());
+
+            if (params.isNamedCurve())
+            {
+                ASN1ObjectIdentifier oid = ASN1ObjectIdentifier.getInstance(params.getParameters());
+                X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
+
+                if (ecP == null) // GOST Curve
+                {
+                    ECDomainParameters gParam = ECGOST3410NamedCurves.getByOID(oid);
+                    EllipticCurve ellipticCurve = EC5Util.convertCurve(gParam.getCurve(), gParam.getSeed());
+
+                    ecSpec = new ECNamedCurveSpec(
                         ECGOST3410NamedCurves.getName(oid),
                         ellipticCurve,
                         new ECPoint(
-                                gParam.getG().getX().toBigInteger(),
-                                gParam.getG().getY().toBigInteger()),
+                            gParam.getG().getAffineXCoord().toBigInteger(),
+                            gParam.getG().getAffineYCoord().toBigInteger()),
                         gParam.getN(),
                         gParam.getH());
-            }
-            else
-            {
-                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+                }
+                else
+                {
+                    EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
-                ecSpec = new ECNamedCurveSpec(
+                    ecSpec = new ECNamedCurveSpec(
                         ECUtil.getCurveName(oid),
                         ellipticCurve,
                         new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
+                            ecP.getG().getAffineXCoord().toBigInteger(),
+                            ecP.getG().getAffineYCoord().toBigInteger()),
                         ecP.getN(),
                         ecP.getH());
+                }
             }
-        }
-        else if (params.isImplicitlyCA())
-        {
-            ecSpec = null;
-        }
-        else
-        {
-            X9ECParameters      ecP = X9ECParameters.getInstance(params.getParameters());
-            EllipticCurve       ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
+            else if (params.isImplicitlyCA())
+            {
+                ecSpec = null;
+            }
+            else
+            {
+                X9ECParameters ecP = X9ECParameters.getInstance(params.getParameters());
+                EllipticCurve ellipticCurve = EC5Util.convertCurve(ecP.getCurve(), ecP.getSeed());
 
-            this.ecSpec = new ECParameterSpec(
-                ellipticCurve,
-                new ECPoint(
-                        ecP.getG().getX().toBigInteger(),
-                        ecP.getG().getY().toBigInteger()),
-                ecP.getN(),
-                ecP.getH().intValue());
-        }
+                this.ecSpec = new ECParameterSpec(
+                    ellipticCurve,
+                    new ECPoint(
+                        ecP.getG().getAffineXCoord().toBigInteger(),
+                        ecP.getG().getAffineYCoord().toBigInteger()),
+                    ecP.getN(),
+                    ecP.getH().intValue());
+            }
 
-        ASN1Encodable privKey = info.parsePrivateKey();
-        if (privKey instanceof DERInteger)
-        {
-            DERInteger          derD = DERInteger.getInstance(privKey);
+            ASN1Encodable privKey = info.parsePrivateKey();
+            if (privKey instanceof DERInteger)
+            {
+                DERInteger derD = DERInteger.getInstance(privKey);
 
-            this.d = derD.getValue();
-        }
-        else
-        {
-            org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
+                this.d = derD.getValue();
+            }
+            else
+            {
+                org.bouncycastle.asn1.sec.ECPrivateKey ec = org.bouncycastle.asn1.sec.ECPrivateKey.getInstance(privKey);
 
-            this.d = ec.getKey();
-            this.publicKey = ec.getPublicKey();
+                this.d = ec.getKey();
+                this.publicKey = ec.getPublicKey();
+            }
         }
     }
 
@@ -282,64 +328,92 @@
      */
     public byte[] getEncoded()
     {
-        X962Parameters          params;
-
-        if (ecSpec instanceof ECNamedCurveSpec)
+        if (gostParams != null)
         {
-            DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
-            if (curveOid == null)  // guess it's the OID
+            byte[] encKey = new byte[32];
+
+            extractBytes(encKey, 0, this.getS());
+
+            try
             {
-                curveOid = new DERObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+                PrivateKeyInfo info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, gostParams), new DEROctetString(encKey));
+
+                return info.getEncoded(ASN1Encoding.DER);
             }
-            params = new X962Parameters(curveOid);
-        }
-        else if (ecSpec == null)
-        {
-            params = new X962Parameters(DERNull.INSTANCE);
-        }
-        else
-        {
-            ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
-
-            X9ECParameters ecP = new X9ECParameters(
-                curve,
-                EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
-                ecSpec.getOrder(),
-                BigInteger.valueOf(ecSpec.getCofactor()),
-                ecSpec.getCurve().getSeed());
-
-            params = new X962Parameters(ecP);
-        }
-        
-        PrivateKeyInfo          info;
-        org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
-
-        if (publicKey != null)
-        {
-            keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params);
-        }
-        else
-        {
-            keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), params);
-        }
-
-        try
-        {
-            if (algorithm.equals("ECGOST3410"))
+            catch (IOException e)
             {
-                info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive());
+                return null;
+            }
+        }
+        else
+        {
+            X962Parameters params;
+
+            if (ecSpec instanceof ECNamedCurveSpec)
+            {
+                DERObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
+                if (curveOid == null)  // guess it's the OID
+                {
+                    curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
+                }
+                params = new X962Parameters(curveOid);
+            }
+            else if (ecSpec == null)
+            {
+                params = new X962Parameters(DERNull.INSTANCE);
             }
             else
             {
+                ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
 
-                info = new PrivateKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params.toASN1Primitive()), keyStructure.toASN1Primitive());
+                X9ECParameters ecP = new X9ECParameters(
+                    curve,
+                    EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
+                    ecSpec.getOrder(),
+                    BigInteger.valueOf(ecSpec.getCofactor()),
+                    ecSpec.getCurve().getSeed());
+
+                params = new X962Parameters(ecP);
             }
 
-            return info.getEncoded(ASN1Encoding.DER);
+            PrivateKeyInfo info;
+            org.bouncycastle.asn1.sec.ECPrivateKey keyStructure;
+
+            if (publicKey != null)
+            {
+                keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), publicKey, params);
+            }
+            else
+            {
+                keyStructure = new org.bouncycastle.asn1.sec.ECPrivateKey(this.getS(), params);
+            }
+
+            try
+            {
+                info = new PrivateKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params.toASN1Primitive()), keyStructure.toASN1Primitive());
+
+                return info.getEncoded(ASN1Encoding.DER);
+            }
+            catch (IOException e)
+            {
+                return null;
+            }
         }
-        catch (IOException e)
+    }
+
+    private void extractBytes(byte[] encKey, int offSet, BigInteger bI)
+    {
+        byte[] val = bI.toByteArray();
+        if (val.length < 32)
         {
-            return null;
+            byte[] tmp = new byte[32];
+            System.arraycopy(val, 0, tmp, tmp.length - val.length, val.length);
+            val = tmp;
+        }
+
+        for (int i = 0; i != 32; i++)
+        {
+            encKey[offSet + i] = val[val.length - 1 - i];
         }
     }
 
@@ -354,7 +428,7 @@
         {
             return null;
         }
-        
+
         return EC5Util.convertSpec(ecSpec, withCompression);
     }
 
@@ -377,10 +451,10 @@
     {
         return d;
     }
-    
+
     public void setBagAttribute(
         ASN1ObjectIdentifier oid,
-        ASN1Encodable        attribute)
+        ASN1Encodable attribute)
     {
         attrCarrier.setBagAttribute(oid, attribute);
     }
@@ -398,7 +472,7 @@
 
     public void setPointFormat(String style)
     {
-       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
     }
 
     public boolean equals(Object o)
@@ -420,8 +494,8 @@
 
     public String toString()
     {
-        StringBuffer    buf = new StringBuffer();
-        String          nl = System.getProperty("line.separator");
+        StringBuffer buf = new StringBuffer();
+        String nl = System.getProperty("line.separator");
 
         buf.append("EC Private Key").append(nl);
         buf.append("             S: ").append(this.d.toString(16)).append(nl);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java
index b7a1170..650855e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/ecgost/BCECGOST3410PublicKey.java
@@ -11,12 +11,9 @@
 import java.security.spec.EllipticCurve;
 
 import org.bouncycastle.asn1.ASN1Encodable;
-import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.ECGOST3410NamedCurves;
@@ -25,13 +22,9 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ECParameters;
-import org.bouncycastle.asn1.x9.X9ECPoint;
-import org.bouncycastle.asn1.x9.X9IntegerConverter;
-import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.crypto.params.ECDomainParameters;
 import org.bouncycastle.crypto.params.ECPublicKeyParameters;
 import org.bouncycastle.jcajce.provider.asymmetric.util.EC5Util;
-import org.bouncycastle.jcajce.provider.asymmetric.util.ECUtil;
 import org.bouncycastle.jcajce.provider.asymmetric.util.KeyUtil;
 import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
 import org.bouncycastle.jce.interfaces.ECPointEncoder;
@@ -45,12 +38,12 @@
 {
     static final long serialVersionUID = 7026240464295649314L;
 
-    private String                  algorithm = "ECGOST3410";
-    private boolean                 withCompression;
+    private String algorithm = "ECGOST3410";
+    private boolean withCompression;
 
     private transient org.bouncycastle.math.ec.ECPoint q;
-    private transient ECParameterSpec         ecSpec;
-    private transient GOST3410PublicKeyAlgParameters       gostParams;
+    private transient ECParameterSpec ecSpec;
+    private transient GOST3410PublicKeyAlgParameters gostParams;
 
     public BCECGOST3410PublicKey(
         BCECGOST3410PublicKey key)
@@ -60,7 +53,7 @@
         this.withCompression = key.withCompression;
         this.gostParams = key.gostParams;
     }
-    
+
     public BCECGOST3410PublicKey(
         ECPublicKeySpec spec)
     {
@@ -86,18 +79,18 @@
             {
                 org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-                q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false);
-            }               
+                q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
+            }
             this.ecSpec = null;
         }
     }
-    
+
     public BCECGOST3410PublicKey(
         String algorithm,
         ECPublicKeyParameters params,
         ECParameterSpec spec)
     {
-        ECDomainParameters      dp = params.getParameters();
+        ECDomainParameters dp = params.getParameters();
 
         this.algorithm = algorithm;
         this.q = params.getQ();
@@ -119,7 +112,7 @@
         ECPublicKeyParameters params,
         org.bouncycastle.jce.spec.ECParameterSpec spec)
     {
-        ECDomainParameters      dp = params.getParameters();
+        ECDomainParameters dp = params.getParameters();
 
         this.algorithm = algorithm;
         this.q = params.getQ();
@@ -153,14 +146,14 @@
     private ECParameterSpec createSpec(EllipticCurve ellipticCurve, ECDomainParameters dp)
     {
         return new ECParameterSpec(
-                ellipticCurve,
-                new ECPoint(
-                        dp.getG().getX().toBigInteger(),
-                        dp.getG().getY().toBigInteger()),
-                        dp.getN(),
-                        dp.getH().intValue());
+            ellipticCurve,
+            new ECPoint(
+                dp.getG().getAffineXCoord().toBigInteger(),
+                dp.getG().getAffineYCoord().toBigInteger()),
+            dp.getN(),
+            dp.getH().intValue());
     }
-    
+
     public BCECGOST3410PublicKey(
         ECPublicKey key)
     {
@@ -177,125 +170,49 @@
 
     private void populateFromPubKeyInfo(SubjectPublicKeyInfo info)
     {
-        if (info.getAlgorithm().getAlgorithm().equals(CryptoProObjectIdentifiers.gostR3410_2001))
+        DERBitString bits = info.getPublicKeyData();
+        ASN1OctetString key;
+        this.algorithm = "ECGOST3410";
+
+        try
         {
-            DERBitString bits = info.getPublicKeyData();
-            ASN1OctetString key;
-            this.algorithm = "ECGOST3410";
-
-            try
-            {
-                key = (ASN1OctetString) ASN1Primitive.fromByteArray(bits.getBytes());
-            }
-            catch (IOException ex)
-            {
-                throw new IllegalArgumentException("error recovering public key");
-            }
-
-            byte[]          keyEnc = key.getOctets();
-            byte[]          x = new byte[32];
-            byte[]          y = new byte[32];
-
-            for (int i = 0; i != x.length; i++)
-            {
-                x[i] = keyEnc[32 - 1 - i];
-            }
-
-            for (int i = 0; i != y.length; i++)
-            {
-                y[i] = keyEnc[64 - 1 - i];
-            }
-
-            gostParams = new GOST3410PublicKeyAlgParameters((ASN1Sequence)info.getAlgorithm().getParameters());
-
-            ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
-
-            ECCurve curve = spec.getCurve();
-            EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
-
-            this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y), false);
-
-            ecSpec = new ECNamedCurveSpec(
-                    ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
-                    ellipticCurve,
-                    new ECPoint(
-                            spec.getG().getX().toBigInteger(),
-                            spec.getG().getY().toBigInteger()),
-                            spec.getN(), spec.getH());
-
+            key = (ASN1OctetString)ASN1Primitive.fromByteArray(bits.getBytes());
         }
-        else
+        catch (IOException ex)
         {
-            X962Parameters params = new X962Parameters((ASN1Primitive)info.getAlgorithm().getParameters());
-            ECCurve                 curve;
-            EllipticCurve           ellipticCurve;
-
-            if (params.isNamedCurve())
-            {
-                ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)params.getParameters();
-                X9ECParameters ecP = ECUtil.getNamedCurveByOid(oid);
-
-                curve = ecP.getCurve();
-                ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
-
-                ecSpec = new ECNamedCurveSpec(
-                        ECUtil.getCurveName(oid),
-                        ellipticCurve,
-                        new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH());
-            }
-            else if (params.isImplicitlyCA())
-            {
-                ecSpec = null;
-                curve = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa().getCurve();
-            }
-            else
-            {
-                X9ECParameters          ecP = X9ECParameters.getInstance(params.getParameters());
-
-                curve = ecP.getCurve();
-                ellipticCurve = EC5Util.convertCurve(curve, ecP.getSeed());
-
-                this.ecSpec = new ECParameterSpec(
-                        ellipticCurve,
-                        new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
-                        ecP.getN(),
-                        ecP.getH().intValue());
-            }
-
-            DERBitString    bits = info.getPublicKeyData();
-            byte[]          data = bits.getBytes();
-            ASN1OctetString key = new DEROctetString(data);
-
-            //
-            // extra octet string - one of our old certs...
-            //
-            if (data[0] == 0x04 && data[1] == data.length - 2
-                && (data[2] == 0x02 || data[2] == 0x03))
-            {
-                int qLength = new X9IntegerConverter().getByteLength(curve);
-
-                if (qLength >= data.length - 3)
-                {
-                    try
-                    {
-                        key = (ASN1OctetString) ASN1Primitive.fromByteArray(data);
-                    }
-                    catch (IOException ex)
-                    {
-                        throw new IllegalArgumentException("error recovering public key");
-                    }
-                }
-            }
-            X9ECPoint derQ = new X9ECPoint(curve, key);
-
-            this.q = derQ.getPoint();
+            throw new IllegalArgumentException("error recovering public key");
         }
+
+        byte[] keyEnc = key.getOctets();
+        byte[] x = new byte[32];
+        byte[] y = new byte[32];
+
+        for (int i = 0; i != x.length; i++)
+        {
+            x[i] = keyEnc[32 - 1 - i];
+        }
+
+        for (int i = 0; i != y.length; i++)
+        {
+            y[i] = keyEnc[64 - 1 - i];
+        }
+
+        gostParams = GOST3410PublicKeyAlgParameters.getInstance(info.getAlgorithm().getParameters());
+
+        ECNamedCurveParameterSpec spec = ECGOST3410NamedCurveTable.getParameterSpec(ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()));
+
+        ECCurve curve = spec.getCurve();
+        EllipticCurve ellipticCurve = EC5Util.convertCurve(curve, spec.getSeed());
+
+        this.q = curve.createPoint(new BigInteger(1, x), new BigInteger(1, y));
+
+        ecSpec = new ECNamedCurveSpec(
+            ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
+            ellipticCurve,
+            new ECPoint(
+                spec.getG().getAffineXCoord().toBigInteger(),
+                spec.getG().getAffineYCoord().toBigInteger()),
+            spec.getN(), spec.getH());
     }
 
     public String getAlgorithm()
@@ -310,71 +227,23 @@
 
     public byte[] getEncoded()
     {
-        ASN1Encodable        params;
+        ASN1Encodable params;
         SubjectPublicKeyInfo info;
 
-        if (algorithm.equals("ECGOST3410"))
+        if (gostParams != null)
         {
-            if (gostParams != null)
-            {
-                params = gostParams;
-            }
-            else
-            {
-                if (ecSpec instanceof ECNamedCurveSpec)
-                {
-                    params = new GOST3410PublicKeyAlgParameters(
-                                   ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
-                                   CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet);
-                }
-                else
-                {   // strictly speaking this may not be applicable...
-                    ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
-
-                    X9ECParameters ecP = new X9ECParameters(
-                        curve,
-                        EC5Util.convertPoint(curve, ecSpec.getGenerator(), withCompression),
-                        ecSpec.getOrder(),
-                        BigInteger.valueOf(ecSpec.getCofactor()),
-                        ecSpec.getCurve().getSeed());
-
-                    params = new X962Parameters(ecP);
-                }
-            }
-
-            BigInteger      bX = this.q.getX().toBigInteger();
-            BigInteger      bY = this.q.getY().toBigInteger();
-            byte[]          encKey = new byte[64];
-
-            extractBytes(encKey, 0, bX);
-            extractBytes(encKey, 32, bY);
-
-            try
-            {
-                info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey));
-            }
-            catch (IOException e)
-            {
-                return null;
-            }
+            params = gostParams;
         }
         else
         {
             if (ecSpec instanceof ECNamedCurveSpec)
             {
-                ASN1ObjectIdentifier curveOid = ECUtil.getNamedCurveOid(((ECNamedCurveSpec)ecSpec).getName());
-                if (curveOid == null)
-                {
-                    curveOid = new ASN1ObjectIdentifier(((ECNamedCurveSpec)ecSpec).getName());
-                }
-                params = new X962Parameters(curveOid);
-            }
-            else if (ecSpec == null)
-            {
-                params = new X962Parameters(DERNull.INSTANCE);
+                params = new GOST3410PublicKeyAlgParameters(
+                    ECGOST3410NamedCurves.getOID(((ECNamedCurveSpec)ecSpec).getName()),
+                    CryptoProObjectIdentifiers.gostR3411_94_CryptoProParamSet);
             }
             else
-            {
+            {   // strictly speaking this may not be applicable...
                 ECCurve curve = EC5Util.convertCurve(ecSpec.getCurve());
 
                 X9ECParameters ecP = new X9ECParameters(
@@ -386,12 +255,22 @@
 
                 params = new X962Parameters(ecP);
             }
+        }
 
-            ECCurve curve = this.engineGetQ().getCurve();
-            ASN1OctetString p = (ASN1OctetString)
-                new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
+        BigInteger bX = this.q.getAffineXCoord().toBigInteger();
+        BigInteger bY = this.q.getAffineYCoord().toBigInteger();
+        byte[] encKey = new byte[64];
 
-            info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
+        extractBytes(encKey, 0, bX);
+        extractBytes(encKey, 32, bY);
+
+        try
+        {
+            info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3410_2001, params), new DEROctetString(encKey));
+        }
+        catch (IOException e)
+        {
+            return null;
         }
 
         return KeyUtil.getEncodedSubjectPublicKeyInfo(info);
@@ -430,7 +309,7 @@
 
     public ECPoint getW()
     {
-        return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
+        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -439,11 +318,11 @@
         {
             if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
             {
-                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
             else
             {
-                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
         }
 
@@ -467,19 +346,19 @@
 
     public String toString()
     {
-        StringBuffer    buf = new StringBuffer();
-        String          nl = System.getProperty("line.separator");
+        StringBuffer buf = new StringBuffer();
+        String nl = System.getProperty("line.separator");
 
         buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
+        buf.append("            X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
+        buf.append("            Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
 
         return buf.toString();
     }
-    
+
     public void setPointFormat(String style)
     {
-       withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
+        withCompression = !("UNCOMPRESSED".equalsIgnoreCase(style));
     }
 
     public boolean equals(Object o)
@@ -518,4 +397,9 @@
 
         out.writeObject(this.getEncoded());
     }
+
+    public GOST3410PublicKeyAlgParameters getGostParams()
+    {
+        return gostParams;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java
index ce0e603..a2114fa 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/rsa/BCRSAPublicKey.java
@@ -1,6 +1,9 @@
 package org.bouncycastle.jcajce.provider.asymmetric.rsa;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.OptionalDataException;
 import java.math.BigInteger;
 import java.security.interfaces.RSAPublicKey;
 import java.security.spec.RSAPublicKeySpec;
@@ -15,14 +18,18 @@
 public class BCRSAPublicKey
     implements RSAPublicKey
 {
+    private static final AlgorithmIdentifier DEFAULT_ALGORITHM_IDENTIFIER = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
+
     static final long serialVersionUID = 2675817738516720772L;
-    
+
     private BigInteger modulus;
     private BigInteger publicExponent;
+    private transient AlgorithmIdentifier algorithmIdentifier;
 
     BCRSAPublicKey(
         RSAKeyParameters key)
     {
+        this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER;
         this.modulus = key.getModulus();
         this.publicExponent = key.getExponent();
     }
@@ -30,6 +37,7 @@
     BCRSAPublicKey(
         RSAPublicKeySpec spec)
     {
+        this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER;
         this.modulus = spec.getModulus();
         this.publicExponent = spec.getPublicExponent();
     }
@@ -37,6 +45,7 @@
     BCRSAPublicKey(
         RSAPublicKey key)
     {
+        this.algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER;
         this.modulus = key.getModulus();
         this.publicExponent = key.getPublicExponent();
     }
@@ -44,10 +53,16 @@
     BCRSAPublicKey(
         SubjectPublicKeyInfo info)
     {
+        populateFromPublicKeyInfo(info);
+    }
+
+    private void populateFromPublicKeyInfo(SubjectPublicKeyInfo info)
+    {
         try
         {
             org.bouncycastle.asn1.pkcs.RSAPublicKey  pubKey = org.bouncycastle.asn1.pkcs.RSAPublicKey.getInstance(info.parsePublicKey());
 
+            this.algorithmIdentifier = info.getAlgorithm();
             this.modulus = pubKey.getModulus();
             this.publicExponent = pubKey.getPublicExponent();
         }
@@ -89,7 +104,7 @@
 
     public byte[] getEncoded()
     {
-        return KeyUtil.getEncodedSubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE), new org.bouncycastle.asn1.pkcs.RSAPublicKey(getModulus(), getPublicExponent()));
+        return KeyUtil.getEncodedSubjectPublicKeyInfo(algorithmIdentifier, new org.bouncycastle.asn1.pkcs.RSAPublicKey(getModulus(), getPublicExponent()));
     }
 
     public int hashCode()
@@ -126,4 +141,32 @@
 
         return buf.toString();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        try
+        {
+            algorithmIdentifier = AlgorithmIdentifier.getInstance(in.readObject());
+        }
+        catch (OptionalDataException e)
+        {
+            algorithmIdentifier = DEFAULT_ALGORITHM_IDENTIFIER;
+        }
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        if (!algorithmIdentifier.equals(DEFAULT_ALGORITHM_IDENTIFIER))
+        {
+            out.writeObject(algorithmIdentifier.getEncoded());
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
index d4065ac..5eea1b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/util/EC5Util.java
@@ -74,8 +74,8 @@
                 ((ECNamedCurveParameterSpec)spec).getName(),
                 ellipticCurve,
                 new ECPoint(
-                    spec.getG().getX().toBigInteger(),
-                    spec.getG().getY().toBigInteger()),
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
                 spec.getN(),
                 spec.getH());
         }
@@ -84,8 +84,8 @@
             return new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                    spec.getG().getX().toBigInteger(),
-                    spec.getG().getY().toBigInteger()),
+                    spec.getG().getAffineXCoord().toBigInteger(),
+                    spec.getG().getAffineYCoord().toBigInteger()),
                 spec.getN(),
                 spec.getH().intValue());
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java
index 1888328..32e595c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLEntryObject.java
@@ -31,7 +31,7 @@
  * ReasonCode Hode Instruction Code Invalidity Date Certificate Issuer
  * (critical)
  */
-class X509CRLEntryObject extends X509CRLEntry
+public class X509CRLEntryObject extends X509CRLEntry
 {
     private TBSCertList.CRLEntry c;
 
@@ -39,7 +39,7 @@
     private int           hashValue;
     private boolean       isHashValueSet;
 
-    public X509CRLEntryObject(TBSCertList.CRLEntry c)
+    protected X509CRLEntryObject(TBSCertList.CRLEntry c)
     {
         this.c = c;
         this.certificateIssuer = null;
@@ -62,7 +62,7 @@
      * @param previousCertificateIssuer
      *            Certificate issuer of the previous CRLEntry.
      */
-    public X509CRLEntryObject(
+    protected X509CRLEntryObject(
         TBSCertList.CRLEntry c,
         boolean isIndirect,
         X500Name previousCertificateIssuer)
@@ -211,6 +211,23 @@
         return hashValue;
     }
 
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof X509CRLEntryObject)
+        {
+            X509CRLEntryObject other = (X509CRLEntryObject)o;
+
+            return this.c.equals(other.c);
+        }
+
+        return super.equals(this);
+    }
+
     public byte[] getEncoded()
         throws CRLException
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
index 2fc0826..c7d0402 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/asymmetric/x509/X509CRLObject.java
@@ -54,13 +54,15 @@
  * Delta CRL Indicator (critical)
  * Issuing Distribution Point (critical)
  */
-class X509CRLObject
+public class X509CRLObject
     extends X509CRL
 {
     private CertificateList c;
     private String sigAlgName;
     private byte[] sigAlgParams;
     private boolean isIndirect;
+    private boolean isHashCodeSet = false;
+    private int     hashCodeValue;
 
     static boolean isIndirectCRL(X509CRL crl)
         throws CRLException
@@ -78,7 +80,7 @@
         }
     }
 
-    public X509CRLObject(
+    protected X509CRLObject(
         CertificateList c)
         throws CRLException
     {
@@ -522,19 +524,21 @@
             throw new RuntimeException("X.509 CRL used with non X.509 Cert");
         }
 
-        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
+        Enumeration certs = c.getRevokedCertificateEnumeration();
 
         X500Name caName = c.getIssuer();
 
-        if (certs != null)
+        if (certs.hasMoreElements())
         {
             BigInteger serial = ((X509Certificate)cert).getSerialNumber();
 
-            for (int i = 0; i < certs.length; i++)
+            while (certs.hasMoreElements())
             {
-                if (isIndirect && certs[i].hasExtensions())
+                TBSCertList.CRLEntry entry = TBSCertList.CRLEntry.getInstance(certs.nextElement());
+
+                if (isIndirect && entry.hasExtensions())
                 {
-                    Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer);
+                    Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
 
                     if (currentCaName != null)
                     {
@@ -542,7 +546,7 @@
                     }
                 }
 
-                if (certs[i].getUserCertificate().getValue().equals(serial))
+                if (entry.getUserCertificate().getValue().equals(serial))
                 {
                     X500Name issuer;
 
@@ -574,5 +578,50 @@
 
         return false;
     }
+
+    public boolean equals(Object other)
+    {
+        if (this == other)
+        {
+            return true;
+        }
+
+        if (!(other instanceof X509CRL))
+        {
+            return false;
+        }
+
+        if (other instanceof X509CRLObject)
+        {
+            X509CRLObject crlObject = (X509CRLObject)other;
+
+            if (isHashCodeSet)
+            {
+                boolean otherIsHashCodeSet = crlObject.isHashCodeSet;
+                if (otherIsHashCodeSet)
+                {
+                    if (crlObject.hashCodeValue != hashCodeValue)
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            return this.c.equals(crlObject.c);
+        }
+
+        return super.equals(other);
+    }
+
+    public int hashCode()
+    {
+        if (!isHashCodeSet)
+        {
+            isHashCodeSet = true;
+            hashCodeValue = super.hashCode();
+        }
+
+        return hashCodeValue;
+    }
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java
index 7ff57d3..2112673 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/GOST3411.java
@@ -7,6 +7,7 @@
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory;
 
 public class GOST3411
 {
@@ -46,6 +47,18 @@
         }
     }
 
+    /**
+     * PBEWithHmacGOST3411
+     */
+    public static class PBEWithMacKeyFactory
+        extends PBESecretKeyFactory
+    {
+        public PBEWithMacKeyFactory()
+        {
+            super("PBEwithHmacGOST3411", null, false, PKCS12, GOST3411, 256, 0);
+        }
+    }
+
     public static class KeyGenerator
         extends BaseKeyGenerator
     {
@@ -71,6 +84,9 @@
             provider.addAlgorithm("Alg.Alias.MessageDigest.GOST-3411", "GOST3411");
             provider.addAlgorithm("Alg.Alias.MessageDigest." + CryptoProObjectIdentifiers.gostR3411, "GOST3411");
 
+            provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACGOST3411", PREFIX + "$PBEWithMacKeyFactory");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + CryptoProObjectIdentifiers.gostR3411, "PBEWITHHMACGOST3411");
+
             addHMACAlgorithm(provider, "GOST3411", PREFIX + "$HashMac", PREFIX + "$KeyGenerator");
             addHMACAlias(provider, "GOST3411", CryptoProObjectIdentifiers.gostR3411);
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java
index df5d41a..c7502c7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SHA1.java
@@ -193,7 +193,6 @@
 
             provider.addAlgorithm("SecretKeyFactory.PBEWITHHMACSHA1", PREFIX + "$PBEWithMacKeyFactory");
             provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1", PREFIX + "$PBKDF2WithHmacSHA1UTF8");
-            provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2WithHmacSHA1");
             provider.addAlgorithm("Alg.Alias.SecretKeyFactory.PBKDF2WithHmacSHA1AndUTF8", "PBKDF2WithHmacSHA1");
             provider.addAlgorithm("SecretKeyFactory.PBKDF2WithHmacSHA1And8BIT", PREFIX + "$PBKDF2WithHmacSHA18BIT");
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java
new file mode 100644
index 0000000..8050e35
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/SM3.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.crypto.digests.SM3Digest;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+
+public class SM3
+{
+    private SM3()
+    {
+    }
+
+    static public class Digest
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public Digest()
+        {
+            super(new SM3Digest());
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            Digest d = (Digest)super.clone();
+            d.digest = new SM3Digest((SM3Digest)digest);
+
+            return d;
+        }
+    }
+
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = SM3.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("MessageDigest.SM3", PREFIX + "$Digest");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.SM3", "SM3");
+            provider.addAlgorithm("Alg.Alias.MessageDigest.1.2.156.197.1.401", "SM3");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java
new file mode 100644
index 0000000..1191049
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/digest/Skein.java
@@ -0,0 +1,740 @@
+package org.bouncycastle.jcajce.provider.digest;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.digests.SkeinDigest;
+import org.bouncycastle.crypto.macs.HMac;
+import org.bouncycastle.crypto.macs.SkeinMac;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+
+public class Skein
+{
+    private Skein()
+    {
+    }
+
+    public static class DigestSkein256
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public DigestSkein256(int outputSize)
+        {
+            super(new SkeinDigest(SkeinDigest.SKEIN_256, outputSize));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            BCMessageDigest d = (BCMessageDigest)super.clone();
+            d.digest = new SkeinDigest((SkeinDigest)digest);
+
+            return d;
+        }
+    }
+
+    public static class Digest_256_128
+        extends DigestSkein256
+    {
+        public Digest_256_128()
+        {
+            super(128);
+        }
+    }
+
+    public static class Digest_256_160
+        extends DigestSkein256
+    {
+        public Digest_256_160()
+        {
+            super(160);
+        }
+    }
+
+    public static class Digest_256_224
+        extends DigestSkein256
+    {
+        public Digest_256_224()
+        {
+            super(224);
+        }
+    }
+
+    public static class Digest_256_256
+        extends DigestSkein256
+    {
+        public Digest_256_256()
+        {
+            super(256);
+        }
+    }
+
+    public static class DigestSkein512
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public DigestSkein512(int outputSize)
+        {
+            super(new SkeinDigest(SkeinDigest.SKEIN_512, outputSize));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            BCMessageDigest d = (BCMessageDigest)super.clone();
+            d.digest = new SkeinDigest((SkeinDigest)digest);
+
+            return d;
+        }
+    }
+
+    public static class Digest_512_128
+        extends DigestSkein512
+    {
+        public Digest_512_128()
+        {
+            super(128);
+        }
+    }
+
+    public static class Digest_512_160
+        extends DigestSkein512
+    {
+        public Digest_512_160()
+        {
+            super(160);
+        }
+    }
+
+    public static class Digest_512_224
+        extends DigestSkein512
+    {
+        public Digest_512_224()
+        {
+            super(224);
+        }
+    }
+
+    public static class Digest_512_256
+        extends DigestSkein512
+    {
+        public Digest_512_256()
+        {
+            super(256);
+        }
+    }
+
+    public static class Digest_512_384
+        extends DigestSkein512
+    {
+        public Digest_512_384()
+        {
+            super(384);
+        }
+    }
+
+    public static class Digest_512_512
+        extends DigestSkein512
+    {
+        public Digest_512_512()
+        {
+            super(512);
+        }
+    }
+
+    public static class DigestSkein1024
+        extends BCMessageDigest
+        implements Cloneable
+    {
+        public DigestSkein1024(int outputSize)
+        {
+            super(new SkeinDigest(SkeinDigest.SKEIN_1024, outputSize));
+        }
+
+        public Object clone()
+            throws CloneNotSupportedException
+        {
+            BCMessageDigest d = (BCMessageDigest)super.clone();
+            d.digest = new SkeinDigest((SkeinDigest)digest);
+
+            return d;
+        }
+    }
+
+    public static class Digest_1024_384
+        extends DigestSkein1024
+    {
+        public Digest_1024_384()
+        {
+            super(384);
+        }
+    }
+
+    public static class Digest_1024_512
+        extends DigestSkein1024
+    {
+        public Digest_1024_512()
+        {
+            super(512);
+        }
+    }
+
+    public static class Digest_1024_1024
+        extends DigestSkein1024
+    {
+        public Digest_1024_1024()
+        {
+            super(1024);
+        }
+    }
+
+    /**
+     * Skein HMac
+     */
+    public static class HashMac_256_128
+        extends BaseMac
+    {
+        public HashMac_256_128()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 128)));
+        }
+    }
+
+    public static class HashMac_256_160
+        extends BaseMac
+    {
+        public HashMac_256_160()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 160)));
+        }
+    }
+
+    public static class HashMac_256_224
+        extends BaseMac
+    {
+        public HashMac_256_224()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 224)));
+        }
+    }
+
+    public static class HashMac_256_256
+        extends BaseMac
+    {
+        public HashMac_256_256()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_256, 256)));
+        }
+    }
+
+    public static class HashMac_512_128
+        extends BaseMac
+    {
+        public HashMac_512_128()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 128)));
+        }
+    }
+
+    public static class HashMac_512_160
+        extends BaseMac
+    {
+        public HashMac_512_160()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 160)));
+        }
+    }
+
+    public static class HashMac_512_224
+        extends BaseMac
+    {
+        public HashMac_512_224()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 224)));
+        }
+    }
+
+    public static class HashMac_512_256
+        extends BaseMac
+    {
+        public HashMac_512_256()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 256)));
+        }
+    }
+
+    public static class HashMac_512_384
+        extends BaseMac
+    {
+        public HashMac_512_384()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 384)));
+        }
+    }
+
+    public static class HashMac_512_512
+        extends BaseMac
+    {
+        public HashMac_512_512()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_512, 512)));
+        }
+    }
+
+    public static class HashMac_1024_384
+        extends BaseMac
+    {
+        public HashMac_1024_384()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 384)));
+        }
+    }
+
+    public static class HashMac_1024_512
+        extends BaseMac
+    {
+        public HashMac_1024_512()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 512)));
+        }
+    }
+
+    public static class HashMac_1024_1024
+        extends BaseMac
+    {
+        public HashMac_1024_1024()
+        {
+            super(new HMac(new SkeinDigest(SkeinDigest.SKEIN_1024, 1024)));
+        }
+    }
+
+    public static class HMacKeyGenerator_256_128
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_256_128()
+        {
+            super("HMACSkein-256-128", 128, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_256_160
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_256_160()
+        {
+            super("HMACSkein-256-160", 160, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_256_224
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_256_224()
+        {
+            super("HMACSkein-256-224", 224, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_256_256
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_256_256()
+        {
+            super("HMACSkein-256-256", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_512_128
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_512_128()
+        {
+            super("HMACSkein-512-128", 128, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_512_160
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_512_160()
+        {
+            super("HMACSkein-512-160", 160, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_512_224
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_512_224()
+        {
+            super("HMACSkein-512-224", 224, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_512_256
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_512_256()
+        {
+            super("HMACSkein-512-256", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_512_384
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_512_384()
+        {
+            super("HMACSkein-512-384", 384, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_512_512
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_512_512()
+        {
+            super("HMACSkein-512-512", 512, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_1024_384
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_1024_384()
+        {
+            super("HMACSkein-1024-384", 384, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_1024_512
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_1024_512()
+        {
+            super("HMACSkein-1024-512", 512, new CipherKeyGenerator());
+        }
+    }
+
+    public static class HMacKeyGenerator_1024_1024
+        extends BaseKeyGenerator
+    {
+        public HMacKeyGenerator_1024_1024()
+        {
+            super("HMACSkein-1024-1024", 1024, new CipherKeyGenerator());
+        }
+    }
+
+    /*
+     * Skein-MAC
+     */
+    public static class SkeinMac_256_128
+        extends BaseMac
+    {
+        public SkeinMac_256_128()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_256, 128));
+        }
+    }
+
+    public static class SkeinMac_256_160
+        extends BaseMac
+    {
+        public SkeinMac_256_160()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_256, 160));
+        }
+    }
+
+    public static class SkeinMac_256_224
+        extends BaseMac
+    {
+        public SkeinMac_256_224()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_256, 224));
+        }
+    }
+
+    public static class SkeinMac_256_256
+        extends BaseMac
+    {
+        public SkeinMac_256_256()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_256, 256));
+        }
+    }
+
+    public static class SkeinMac_512_128
+        extends BaseMac
+    {
+        public SkeinMac_512_128()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_512, 128));
+        }
+    }
+
+    public static class SkeinMac_512_160
+        extends BaseMac
+    {
+        public SkeinMac_512_160()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_512, 160));
+        }
+    }
+
+    public static class SkeinMac_512_224
+        extends BaseMac
+    {
+        public SkeinMac_512_224()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_512, 224));
+        }
+    }
+
+    public static class SkeinMac_512_256
+        extends BaseMac
+    {
+        public SkeinMac_512_256()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_512, 256));
+        }
+    }
+
+    public static class SkeinMac_512_384
+        extends BaseMac
+    {
+        public SkeinMac_512_384()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_512, 384));
+        }
+    }
+
+    public static class SkeinMac_512_512
+        extends BaseMac
+    {
+        public SkeinMac_512_512()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_512, 512));
+        }
+    }
+
+    public static class SkeinMac_1024_384
+        extends BaseMac
+    {
+        public SkeinMac_1024_384()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_1024, 384));
+        }
+    }
+
+    public static class SkeinMac_1024_512
+        extends BaseMac
+    {
+        public SkeinMac_1024_512()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_1024, 512));
+        }
+    }
+
+    public static class SkeinMac_1024_1024
+        extends BaseMac
+    {
+        public SkeinMac_1024_1024()
+        {
+            super(new SkeinMac(SkeinMac.SKEIN_1024, 1024));
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_256_128
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_256_128()
+        {
+            super("Skein-MAC-256-128", 128, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_256_160
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_256_160()
+        {
+            super("Skein-MAC-256-160", 160, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_256_224
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_256_224()
+        {
+            super("Skein-MAC-256-224", 224, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_256_256
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_256_256()
+        {
+            super("Skein-MAC-256-256", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_512_128
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_512_128()
+        {
+            super("Skein-MAC-512-128", 128, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_512_160
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_512_160()
+        {
+            super("Skein-MAC-512-160", 160, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_512_224
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_512_224()
+        {
+            super("Skein-MAC-512-224", 224, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_512_256
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_512_256()
+        {
+            super("Skein-MAC-512-256", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_512_384
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_512_384()
+        {
+            super("Skein-MAC-512-384", 384, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_512_512
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_512_512()
+        {
+            super("Skein-MAC-512-512", 512, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_1024_384
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_1024_384()
+        {
+            super("Skein-MAC-1024-384", 384, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_1024_512
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_1024_512()
+        {
+            super("Skein-MAC-1024-512", 512, new CipherKeyGenerator());
+        }
+    }
+
+    public static class SkeinMacKeyGenerator_1024_1024
+        extends BaseKeyGenerator
+    {
+        public SkeinMacKeyGenerator_1024_1024()
+        {
+            super("Skein-MAC-1024-1024", 1024, new CipherKeyGenerator());
+        }
+    }
+
+    public static class Mappings
+        extends DigestAlgorithmProvider
+    {
+        private static final String PREFIX = Skein.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            // Skein sizes as used in "The Skein Hash Function Family 1.3"
+
+            provider.addAlgorithm("MessageDigest.Skein-256-128", PREFIX + "$Digest_256_128");
+            provider.addAlgorithm("MessageDigest.Skein-256-160", PREFIX + "$Digest_256_160");
+            provider.addAlgorithm("MessageDigest.Skein-256-224", PREFIX + "$Digest_256_224");
+            provider.addAlgorithm("MessageDigest.Skein-256-256", PREFIX + "$Digest_256_256");
+
+            provider.addAlgorithm("MessageDigest.Skein-512-128", PREFIX + "$Digest_512_128");
+            provider.addAlgorithm("MessageDigest.Skein-512-160", PREFIX + "$Digest_512_160");
+            provider.addAlgorithm("MessageDigest.Skein-512-224", PREFIX + "$Digest_512_224");
+            provider.addAlgorithm("MessageDigest.Skein-512-256", PREFIX + "$Digest_512_256");
+            provider.addAlgorithm("MessageDigest.Skein-512-384", PREFIX + "$Digest_512_384");
+            provider.addAlgorithm("MessageDigest.Skein-512-512", PREFIX + "$Digest_512_512");
+
+            provider.addAlgorithm("MessageDigest.Skein-1024-384", PREFIX + "$Digest_1024_384");
+            provider.addAlgorithm("MessageDigest.Skein-1024-512", PREFIX + "$Digest_1024_512");
+            provider.addAlgorithm("MessageDigest.Skein-1024-1024", PREFIX + "$Digest_1024_1024");
+
+            addHMACAlgorithm(provider, "Skein-256-128", PREFIX + "$HashMac_256_128", PREFIX + "$HMacKeyGenerator_256_128");
+            addHMACAlgorithm(provider, "Skein-256-160", PREFIX + "$HashMac_256_160", PREFIX + "$HMacKeyGenerator_256_160");
+            addHMACAlgorithm(provider, "Skein-256-224", PREFIX + "$HashMac_256_224", PREFIX + "$HMacKeyGenerator_256_224");
+            addHMACAlgorithm(provider, "Skein-256-256", PREFIX + "$HashMac_256_256", PREFIX + "$HMacKeyGenerator_256_256");
+
+            addHMACAlgorithm(provider, "Skein-512-128", PREFIX + "$HashMac_512_128", PREFIX + "$HMacKeyGenerator_512_128");
+            addHMACAlgorithm(provider, "Skein-512-160", PREFIX + "$HashMac_512_160", PREFIX + "$HMacKeyGenerator_512_160");
+            addHMACAlgorithm(provider, "Skein-512-224", PREFIX + "$HashMac_512_224", PREFIX + "$HMacKeyGenerator_512_224");
+            addHMACAlgorithm(provider, "Skein-512-256", PREFIX + "$HashMac_512_256", PREFIX + "$HMacKeyGenerator_512_256");
+            addHMACAlgorithm(provider, "Skein-512-384", PREFIX + "$HashMac_512_384", PREFIX + "$HMacKeyGenerator_512_384");
+            addHMACAlgorithm(provider, "Skein-512-512", PREFIX + "$HashMac_512_512", PREFIX + "$HMacKeyGenerator_512_512");
+
+            addHMACAlgorithm(provider, "Skein-1024-384", PREFIX + "$HashMac_1024_384", PREFIX + "$HMacKeyGenerator_1024_384");
+            addHMACAlgorithm(provider, "Skein-1024-512", PREFIX + "$HashMac_1024_512", PREFIX + "$HMacKeyGenerator_1024_512");
+            addHMACAlgorithm(provider, "Skein-1024-1024", PREFIX + "$HashMac_1024_1024", PREFIX + "$HMacKeyGenerator_1024_1024");
+
+            addSkeinMacAlgorithm(provider, 256, 128);
+            addSkeinMacAlgorithm(provider, 256, 160);
+            addSkeinMacAlgorithm(provider, 256, 224);
+            addSkeinMacAlgorithm(provider, 256, 256);
+
+            addSkeinMacAlgorithm(provider, 512, 128);
+            addSkeinMacAlgorithm(provider, 512, 160);
+            addSkeinMacAlgorithm(provider, 512, 224);
+            addSkeinMacAlgorithm(provider, 512, 256);
+            addSkeinMacAlgorithm(provider, 512, 384);
+            addSkeinMacAlgorithm(provider, 512, 512);
+
+            addSkeinMacAlgorithm(provider, 1024, 384);
+            addSkeinMacAlgorithm(provider, 1024, 512);
+            addSkeinMacAlgorithm(provider, 1024, 1024);
+        }
+
+        private void addSkeinMacAlgorithm(ConfigurableProvider provider, int blockSize, int outputSize)
+        {
+            String mainName = "Skein-MAC-" + blockSize + "-" + outputSize;
+            String algorithmClassName = PREFIX + "$SkeinMac_" + blockSize + "_" + outputSize;
+            String keyGeneratorClassName = PREFIX + "$SkeinMacKeyGenerator_" + blockSize + "_" + outputSize;
+
+            provider.addAlgorithm("Mac." + mainName, algorithmClassName);
+            provider.addAlgorithm("Alg.Alias.Mac.Skein-MAC" + blockSize + "/" + outputSize, mainName);
+            provider.addAlgorithm("KeyGenerator." + mainName, keyGeneratorClassName);
+            provider.addAlgorithm("Alg.Alias.KeyGenerator.Skein-MAC" + blockSize + "/" + outputSize, mainName);
+        }
+
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
index c255002..9a62c98 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/keystore/pkcs12/PKCS12KeyStoreSpi.java
@@ -6,6 +6,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.KeyStore;
 import java.security.KeyStore.LoadStoreParameter;
@@ -24,13 +26,18 @@
 import java.security.cert.CertificateException;
 import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
+import java.security.spec.InvalidKeySpecException;
+import java.util.Collections;
 import java.util.Date;
 import java.util.Enumeration;
+import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.Map;
 import java.util.Vector;
 
 import javax.crypto.Cipher;
 import javax.crypto.Mac;
+import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.SecretKeyFactory;
 import javax.crypto.spec.IvParameterSpec;
@@ -54,6 +61,10 @@
 import org.bouncycastle.asn1.DEROutputStream;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.DERSet;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.AuthenticatedSafe;
 import org.bouncycastle.asn1.pkcs.CertBag;
 import org.bouncycastle.asn1.pkcs.ContentInfo;
@@ -75,12 +86,14 @@
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.jcajce.provider.config.PKCS12StoreParameter;
 import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
-import org.bouncycastle.jcajce.provider.util.SecretKeyUtil;
+import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
 import org.bouncycastle.jce.interfaces.BCKeyStore;
 import org.bouncycastle.jce.interfaces.PKCS12BagAttributeCarrier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.jce.provider.JDKPKCS12StoreParameter;
 import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Hex;
 
@@ -92,6 +105,7 @@
     private static final int MIN_ITERATIONS = 1024;
 
     private static final Provider bcProvider = new BouncyCastleProvider();
+    private static final DefaultSecretKeyProvider keySizeProvider = new DefaultSecretKeyProvider();
 
     private IgnoresCaseHashtable keys = new IgnoresCaseHashtable();
     private Hashtable localIds = new Hashtable();
@@ -593,16 +607,8 @@
             }
             else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
             {
-                PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters());
-                PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
 
-                SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider);
-
-                SecretKey k = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), SecretKeyUtil.getKeySize(alg.getEncryptionScheme().getAlgorithm())));
-
-                Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId(), bcProvider);
-
-                cipher.init(Cipher.UNWRAP_MODE, k, new IvParameterSpec(ASN1OctetString.getInstance(alg.getEncryptionScheme().getParameters()).getOctets()));
+                Cipher cipher = createCipher(Cipher.UNWRAP_MODE, password, algId);
 
                 // we pass "" as the key algorithm type as it is unknown at this point
                 return (PrivateKey)cipher.unwrap(data, "", Cipher.PRIVATE_KEY);
@@ -656,29 +662,88 @@
         byte[] data)
         throws IOException
     {
-        String algorithm = algId.getAlgorithm().getId();
-        PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters());
-        PBEKeySpec pbeSpec = new PBEKeySpec(password);
+        ASN1ObjectIdentifier algorithm = algId.getAlgorithm();
 
-        try
+        if (algorithm.on(PKCSObjectIdentifiers.pkcs_12PbeIds))
         {
-            SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm, bcProvider);
-            PBEParameterSpec defParams = new PBEParameterSpec(
-                pbeParams.getIV(),
-                pbeParams.getIterations().intValue());
-            BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec);
+            PKCS12PBEParams pbeParams = PKCS12PBEParams.getInstance(algId.getParameters());
+            PBEKeySpec pbeSpec = new PBEKeySpec(password);
 
-            key.setTryWrongPKCS12Zero(wrongPKCS12Zero);
+            try
+            {
+                SecretKeyFactory keyFact = SecretKeyFactory.getInstance(algorithm.getId(), bcProvider);
+                PBEParameterSpec defParams = new PBEParameterSpec(
+                    pbeParams.getIV(),
+                    pbeParams.getIterations().intValue());
+                BCPBEKey key = (BCPBEKey)keyFact.generateSecret(pbeSpec);
 
-            Cipher cipher = Cipher.getInstance(algorithm, bcProvider);
-            int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
-            cipher.init(mode, key, defParams);
-            return cipher.doFinal(data);
+                key.setTryWrongPKCS12Zero(wrongPKCS12Zero);
+
+                Cipher cipher = Cipher.getInstance(algorithm.getId(), bcProvider);
+                int mode = forEncryption ? Cipher.ENCRYPT_MODE : Cipher.DECRYPT_MODE;
+                cipher.init(mode, key, defParams);
+                return cipher.doFinal(data);
+            }
+            catch (Exception e)
+            {
+                throw new IOException("exception decrypting data - " + e.toString());
+            }
         }
-        catch (Exception e)
+        else  if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
         {
-            throw new IOException("exception decrypting data - " + e.toString());
+            try
+            {
+                Cipher cipher = createCipher(Cipher.DECRYPT_MODE, password, algId);
+
+                return cipher.doFinal(data);
+            }
+            catch (Exception e)
+            {
+                throw new IOException("exception decrypting data - " + e.toString());
+            }
         }
+        else
+        {
+            throw new IOException("unknown PBE algorithm: " + algorithm);
+        }
+    }
+
+    private Cipher createCipher(int mode, char[] password, AlgorithmIdentifier algId)
+        throws NoSuchAlgorithmException, InvalidKeySpecException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException
+    {
+        PBES2Parameters alg = PBES2Parameters.getInstance(algId.getParameters());
+        PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
+        AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+        SecretKeyFactory keyFact = SecretKeyFactory.getInstance(alg.getKeyDerivationFunc().getAlgorithm().getId(), bcProvider);
+        SecretKey key;
+
+        if (func.isDefaultPrf())
+        {
+            key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+        }
+        else
+        {
+            key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+        }
+
+        Cipher cipher = Cipher.getInstance(alg.getEncryptionScheme().getAlgorithm().getId());
+
+        AlgorithmIdentifier encryptionAlg = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+        ASN1Encodable encParams = alg.getEncryptionScheme().getParameters();
+        if (encParams instanceof ASN1OctetString)
+        {
+            cipher.init(mode, key, new IvParameterSpec(ASN1OctetString.getInstance(encParams).getOctets()));
+        }
+        else
+        {
+            // TODO: at the moment it's just GOST, but...
+            GOST28147Parameters gParams = GOST28147Parameters.getInstance(encParams);
+
+            cipher.init(mode, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV()));
+        }
+        return cipher;
     }
 
     public void engineLoad(
@@ -1671,4 +1736,43 @@
             return orig.elements();
         }
     }
+
+    private static class DefaultSecretKeyProvider
+    {
+        private final Map KEY_SIZES;
+
+        DefaultSecretKeyProvider()
+        {
+            Map keySizes = new HashMap();
+
+            keySizes.put(new ASN1ObjectIdentifier("1.2.840.113533.7.66.10"), Integers.valueOf(128));
+
+            keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC.getId(), Integers.valueOf(192));
+
+            keySizes.put(NISTObjectIdentifiers.id_aes128_CBC, Integers.valueOf(128));
+            keySizes.put(NISTObjectIdentifiers.id_aes192_CBC, Integers.valueOf(192));
+            keySizes.put(NISTObjectIdentifiers.id_aes256_CBC, Integers.valueOf(256));
+
+            keySizes.put(NTTObjectIdentifiers.id_camellia128_cbc, Integers.valueOf(128));
+            keySizes.put(NTTObjectIdentifiers.id_camellia192_cbc, Integers.valueOf(192));
+            keySizes.put(NTTObjectIdentifiers.id_camellia256_cbc, Integers.valueOf(256));
+
+            keySizes.put(CryptoProObjectIdentifiers.gostR28147_gcfb, Integers.valueOf(256));
+
+            KEY_SIZES = Collections.unmodifiableMap(keySizes);
+        }
+
+        public int getKeySize(AlgorithmIdentifier algorithmIdentifier)
+        {
+            // TODO: not all ciphers/oid relationships are this simple.
+            Integer keySize = (Integer)KEY_SIZES.get(algorithmIdentifier.getAlgorithm());
+
+            if (keySize != null)
+            {
+                return keySize.intValue();
+            }
+
+            return -1;
+        }
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
index 7a6f7b0..a600604 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/AES.java
@@ -1,13 +1,18 @@
 package org.bouncycastle.jcajce.provider.symmetric;
 
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
 
 import javax.crypto.spec.IvParameterSpec;
 
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.cms.GCMParameters;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
@@ -15,6 +20,7 @@
 import org.bouncycastle.crypto.engines.AESFastEngine;
 import org.bouncycastle.crypto.engines.AESWrapEngine;
 import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.CMac;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
@@ -23,6 +29,7 @@
 import org.bouncycastle.crypto.modes.OFBBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
@@ -31,9 +38,12 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jcajce.provider.symmetric.util.PBESecretKeyFactory;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Integers;
 
 public final class AES
 {
+    private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+
     private AES()
     {
     }
@@ -80,6 +90,15 @@
         }
     }
 
+    static public class GCM
+        extends BaseBlockCipher
+    {
+        public GCM()
+        {
+            super(new GCMBlockCipher(new AESFastEngine()));
+        }
+    }
+
     public static class AESCMAC
         extends BaseMac
     {
@@ -98,6 +117,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new AESFastEngine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-AES", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     static public class Wrap
         extends BaseWrapCipher
     {
@@ -325,6 +362,95 @@
         }
     }
 
+    public static class AlgParamsGCM
+        extends BaseAlgorithmParameters
+    {
+        private GCMParameters gcmParams;
+
+        protected void engineInit(AlgorithmParameterSpec paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (gcmSpecClass != null)
+            {
+                try
+                {
+                    Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
+                    Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
+
+
+                    gcmParams = new GCMParameters((byte[])iv.invoke(paramSpec, new Object[0]), ((Integer)tLen.invoke(paramSpec, new Object[0])).intValue());
+                }
+                catch (Exception e)
+                {
+                    throw new InvalidParameterSpecException("Cannot process GCMParameterSpec.");
+                }
+            }
+        }
+
+        protected void engineInit(byte[] params)
+            throws IOException
+        {
+            gcmParams = GCMParameters.getInstance(params);
+        }
+
+        protected void engineInit(byte[] params, String format)
+            throws IOException
+        {
+            if (!isASN1FormatString(format))
+            {
+                throw new IOException("unknown format specified");
+            }
+
+            gcmParams = GCMParameters.getInstance(params);
+        }
+
+        protected byte[] engineGetEncoded()
+            throws IOException
+        {
+            return gcmParams.getEncoded();
+        }
+
+        protected byte[] engineGetEncoded(String format)
+            throws IOException
+        {
+            if (!isASN1FormatString(format))
+            {
+                throw new IOException("unknown format specified");
+            }
+
+            return gcmParams.getEncoded();
+        }
+
+        protected String engineToString()
+        {
+            return "GCM";
+        }
+
+        protected AlgorithmParameterSpec localEngineGetParameterSpec(Class paramSpec)
+            throws InvalidParameterSpecException
+        {
+            if (gcmSpecClass != null)
+            {
+                try
+                {
+                    Constructor constructor = gcmSpecClass.getConstructor(new Class[] { byte[].class, Integer.class });
+
+                    return (AlgorithmParameterSpec)constructor.newInstance(new Object[] { gcmParams.getNonce(), Integers.valueOf(gcmParams.getIcvLen()) });
+                }
+                catch (NoSuchMethodException e)
+                {
+                    throw new InvalidParameterSpecException("no constructor found!");   // should never happen
+                }
+                catch (Exception e)
+                {
+                    throw new InvalidParameterSpecException("construction failed: " + e.getMessage());   // should never happen
+                }
+            }
+
+            throw new InvalidParameterSpecException("unknown parameter spec: " + paramSpec.getName());
+        }
+    }
+
     public static class Mappings
         extends SymmetricAlgorithmProvider
     {
@@ -353,6 +479,11 @@
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes192_CBC, "AES");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes256_CBC, "AES");
 
+            provider.addAlgorithm("AlgorithmParameters.GCM", PREFIX + "$AlgParamsGCM");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes128_GCM, "GCM");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes192_GCM, "GCM");
+            provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + NISTObjectIdentifiers.id_aes256_GCM, "GCM");
+
             provider.addAlgorithm("AlgorithmParameterGenerator.AES", PREFIX + "$AlgParamGen");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + wrongAES128, "AES");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameterGenerator." + wrongAES192, "AES");
@@ -383,6 +514,11 @@
             provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_wrap, "AESWRAP");
             provider.addAlgorithm("Cipher.AESRFC3211WRAP", PREFIX + "$RFC3211Wrap");
 
+            provider.addAlgorithm("Cipher.GCM", PREFIX + "$GCM");
+            provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes128_GCM, "GCM");
+            provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes192_GCM, "GCM");
+            provider.addAlgorithm("Alg.Alias.Cipher." + NISTObjectIdentifiers.id_aes256_GCM, "GCM");
+
             provider.addAlgorithm("KeyGenerator.AES", PREFIX + "$KeyGen");
             provider.addAlgorithm("KeyGenerator." + wrongAES128, PREFIX + "$KeyGen128");
             provider.addAlgorithm("KeyGenerator." + wrongAES192, PREFIX + "$KeyGen192");
@@ -484,6 +620,21 @@
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + BCObjectIdentifiers.bc_pbe_sha256_pkcs12_aes256_cbc.getId(), "PKCS12PBE");
 
             addGMacAlgorithm(provider, "AES", PREFIX + "$AESGMAC", PREFIX + "$KeyGen128");
+            addPoly1305Algorithm(provider, "AES", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
+        }
+    }
+
+    private static Class lookup(String className)
+    {
+        try
+        {
+            Class def = AES.class.getClassLoader().loadClass(className);
+
+            return def;
+        }
+        catch (Exception e)
+        {
+            return null;
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java
index 68605f4..d16e6c7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/CAST6.java
@@ -1,13 +1,16 @@
 package org.bouncycastle.jcajce.provider.symmetric;
 
+import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.CAST6Engine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
 
 public final class CAST6
 {
@@ -20,7 +23,13 @@
     {
         public ECB()
         {
-            super(new CAST6Engine());
+            super(new BlockCipherProvider()
+            {
+                public BlockCipher get()
+                {
+                    return new CAST6Engine();
+                }
+            });
         }
     }
 
@@ -42,6 +51,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new CAST6Engine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-CAST6", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     public static class Mappings
         extends SymmetricAlgorithmProvider
     {
@@ -57,6 +84,7 @@
             provider.addAlgorithm("KeyGenerator.CAST6", PREFIX + "$KeyGen");
 
             addGMacAlgorithm(provider, "CAST6", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "CAST6", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java
index 38b5ca7..95b5156 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Camellia.java
@@ -13,6 +13,7 @@
 import org.bouncycastle.crypto.engines.CamelliaEngine;
 import org.bouncycastle.crypto.engines.CamelliaWrapEngine;
 import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
@@ -83,6 +84,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new CamelliaEngine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-Camellia", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     public static class KeyGen
         extends BaseKeyGenerator
     {
@@ -213,6 +232,7 @@
             provider.addAlgorithm("KeyGenerator." + NTTObjectIdentifiers.id_camellia256_cbc, PREFIX + "$KeyGen256");
 
             addGMacAlgorithm(provider, "CAMELLIA", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "CAMELLIA", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java
new file mode 100644
index 0000000..ff748ae
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/ChaCha.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.ChaChaEngine;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+
+public final class ChaCha
+{
+    private ChaCha()
+    {
+    }
+    
+    public static class Base
+        extends BaseStreamCipher
+    {
+        public Base()
+        {
+            super(new ChaChaEngine(), 8);
+        }
+    }
+
+    public static class KeyGen
+        extends BaseKeyGenerator
+    {
+        public KeyGen()
+        {
+            super("ChaCha", 128, new CipherKeyGenerator());
+        }
+    }
+
+    public static class Mappings
+        extends AlgorithmProvider
+    {
+        private static final String PREFIX = ChaCha.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+
+            provider.addAlgorithm("Cipher.CHACHA", PREFIX + "$Base");
+            provider.addAlgorithm("KeyGenerator.CHACHA", PREFIX + "$KeyGen");
+
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java
index 389b79a..b3ff96b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/GOST28147.java
@@ -8,10 +8,12 @@
 import javax.crypto.spec.IvParameterSpec;
 
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.GOST28147Engine;
 import org.bouncycastle.crypto.macs.GOST28147Mac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
@@ -45,6 +47,15 @@
         }
     }
 
+    public static class GCFB
+       extends BaseBlockCipher
+    {
+        public GCFB()
+        {
+            super(new BufferedBlockCipher(new GCFBBlockCipher(new GOST28147Engine())), 64);
+        }
+    }
+
     /**
      * GOST28147
      */
@@ -132,12 +143,12 @@
             provider.addAlgorithm("Cipher.GOST28147", PREFIX + "$ECB");
             provider.addAlgorithm("Alg.Alias.Cipher.GOST", "GOST28147");
             provider.addAlgorithm("Alg.Alias.Cipher.GOST-28147", "GOST28147");
-            provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.gostR28147_cbc, PREFIX + "$CBC");
+            provider.addAlgorithm("Cipher." + CryptoProObjectIdentifiers.gostR28147_gcfb, PREFIX + "$GCFB");
 
             provider.addAlgorithm("KeyGenerator.GOST28147", PREFIX + "$KeyGen");
             provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST", "GOST28147");
             provider.addAlgorithm("Alg.Alias.KeyGenerator.GOST-28147", "GOST28147");
-            provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_cbc, "GOST28147");
+            provider.addAlgorithm("Alg.Alias.KeyGenerator." + CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147");
 
             provider.addAlgorithm("Mac.GOST28147MAC", PREFIX + "$Mac");
             provider.addAlgorithm("Alg.Alias.Mac.GOST28147", "GOST28147MAC");
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java
index 2d089cc..a92f21d 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Noekeon.java
@@ -7,8 +7,10 @@
 
 import javax.crypto.spec.IvParameterSpec;
 
+import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.NoekeonEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
@@ -16,6 +18,7 @@
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseMac;
+import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
 import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 
@@ -30,7 +33,13 @@
     {
         public ECB()
         {
-            super(new NoekeonEngine());
+            super(new BlockCipherProvider()
+            {
+                public BlockCipher get()
+                {
+                    return new NoekeonEngine();
+                }
+            });
         }
     }
 
@@ -52,6 +61,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new NoekeonEngine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-Noekeon", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     public static class AlgParamGen
         extends BaseAlgorithmParameterGenerator
     {
@@ -120,6 +147,7 @@
             provider.addAlgorithm("KeyGenerator.NOEKEON", PREFIX + "$KeyGen");
 
             addGMacAlgorithm(provider, "NOEKEON", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "NOEKEON", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
index ee3cac9..4b0d8b9 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/PBEPBKDF2.java
@@ -2,17 +2,28 @@
 
 import java.io.IOException;
 import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidKeySpecException;
 import java.security.spec.InvalidParameterSpecException;
+import java.security.spec.KeySpec;
 
+import javax.crypto.SecretKey;
+import javax.crypto.spec.PBEKeySpec;
 import javax.crypto.spec.PBEParameterSpec;
 
 import org.bouncycastle.asn1.ASN1Encoding;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BCPBEKey;
 import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameters;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseSecretKeyFactory;
+import org.bouncycastle.jcajce.provider.symmetric.util.PBE;
 import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
 
 public class PBEPBKDF2
 {
@@ -104,6 +115,99 @@
         }
     }
 
+    public static class BasePBKDF2
+        extends BaseSecretKeyFactory
+    {
+        private int scheme;
+
+        public BasePBKDF2(String name, int scheme)
+        {
+            super(name, PKCSObjectIdentifiers.id_PBKDF2);
+
+            this.scheme = scheme;
+        }
+
+        protected SecretKey engineGenerateSecret(
+            KeySpec keySpec)
+            throws InvalidKeySpecException
+        {
+            if (keySpec instanceof PBEKeySpec)
+            {
+                PBEKeySpec pbeSpec = (PBEKeySpec)keySpec;
+
+                if (pbeSpec.getSalt() == null)
+                {
+                    throw new InvalidKeySpecException("missing required salt");
+                }
+
+                if (pbeSpec.getIterationCount() <= 0)
+                {
+                    throw new InvalidKeySpecException("positive iteration count required: "
+                        + pbeSpec.getIterationCount());
+                }
+
+                if (pbeSpec.getKeyLength() <= 0)
+                {
+                    throw new InvalidKeySpecException("positive key length required: "
+                        + pbeSpec.getKeyLength());
+                }
+
+                if (pbeSpec.getPassword().length == 0)
+                {
+                    throw new IllegalArgumentException("password empty");
+                }
+
+                if (pbeSpec instanceof PBKDF2KeySpec)
+                {
+                    PBKDF2KeySpec spec = (PBKDF2KeySpec)pbeSpec;
+
+                    int digest = getDigestCode(spec.getPrf().getAlgorithm());
+                    int keySize = pbeSpec.getKeyLength();
+                    int ivSize = -1;    // JDK 1,2 and earlier does not understand simplified version.
+                    CipherParameters param = PBE.Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize);
+
+                    return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param);
+                }
+                else
+                {
+                    int digest = SHA1;
+                    int keySize = pbeSpec.getKeyLength();
+                    int ivSize = -1;    // JDK 1,2 and earlier does not understand simplified version.
+                    CipherParameters param = PBE.Util.makePBEMacParameters(pbeSpec, scheme, digest, keySize);
+
+                    return new BCPBEKey(this.algName, this.algOid, scheme, digest, keySize, ivSize, pbeSpec, param);
+                }
+            }
+
+            throw new InvalidKeySpecException("Invalid KeySpec");
+        }
+
+
+        private int getDigestCode(ASN1ObjectIdentifier algorithm)
+            throws InvalidKeySpecException
+        {
+            if (algorithm.equals(CryptoProObjectIdentifiers.gostR3411Hmac))
+            {
+                return GOST3411;
+            }
+            else if (algorithm.equals(PKCSObjectIdentifiers.id_hmacWithSHA1))
+            {
+                return SHA1;
+            }
+
+            throw new InvalidKeySpecException("Invalid KeySpec: unknown PRF algorithm " + algorithm);
+        }
+    }
+
+    public static class PBKDF2withUTF8
+        extends BasePBKDF2
+    {
+        public PBKDF2withUTF8()
+        {
+            super("PBKDF2", PKCS5S2_UTF8);
+        }
+    }
+
     public static class Mappings
         extends AlgorithmProvider
     {
@@ -117,6 +221,8 @@
         {
             provider.addAlgorithm("AlgorithmParameters.PBKDF2", PREFIX + "$AlgParams");
             provider.addAlgorithm("Alg.Alias.AlgorithmParameters." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2");
+            provider.addAlgorithm("SecretKeyFactory.PBKDF2", PREFIX + "$PBKDF2withUTF8");
+            provider.addAlgorithm("Alg.Alias.SecretKeyFactory." + PKCSObjectIdentifiers.id_PBKDF2, "PBKDF2");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java
index a29e717..114c40b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/RC6.java
@@ -11,6 +11,7 @@
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.RC6Engine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.modes.CFBBlockCipher;
@@ -82,6 +83,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new RC6Engine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-RC6", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     public static class KeyGen
         extends BaseKeyGenerator
     {
@@ -155,6 +174,7 @@
             provider.addAlgorithm("AlgorithmParameters.RC6", PREFIX + "$AlgParams");
 
             addGMacAlgorithm(provider, "RC6", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "RC6", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java
index 2ad41bf..e7e257c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SEED.java
@@ -12,6 +12,7 @@
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.SEEDEngine;
 import org.bouncycastle.crypto.engines.SEEDWrapEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
@@ -82,6 +83,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new SEEDEngine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-SEED", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     public static class AlgParamGen
         extends BaseAlgorithmParameterGenerator
     {
@@ -158,6 +177,7 @@
             provider.addAlgorithm("KeyGenerator." + KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap, PREFIX + "$KeyGen");
 
             addGMacAlgorithm(provider, "SEED", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "SEED", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java
index 578de32..ec21880 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Serpent.java
@@ -3,6 +3,8 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.SerpentEngine;
+import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
@@ -51,6 +53,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new TwofishEngine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-Serpent", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     public static class AlgParams
         extends IvAlgorithmParameters
     {
@@ -77,6 +97,7 @@
             provider.addAlgorithm("AlgorithmParameters.Serpent", PREFIX + "$AlgParams");
 
             addGMacAlgorithm(provider, "SERPENT", PREFIX + "$SerpentGMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "SERPENT", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java
new file mode 100644
index 0000000..81666af
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Shacal2.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.SecureRandom;
+import java.security.spec.AlgorithmParameterSpec;
+
+import javax.crypto.spec.IvParameterSpec;
+
+import org.bouncycastle.crypto.BlockCipher;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.Shacal2Engine;
+import org.bouncycastle.crypto.modes.CBCBlockCipher;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseAlgorithmParameterGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BlockCipherProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+public final class Shacal2
+{
+    private Shacal2()
+    {
+    }
+    
+    public static class ECB
+        extends BaseBlockCipher
+    {
+        public ECB()
+        {
+            super(new BlockCipherProvider()
+            {
+                public BlockCipher get()
+                {
+                    return new Shacal2Engine();
+                }
+            });
+        }
+    }
+
+    public static class CBC
+       extends BaseBlockCipher
+    {
+        public CBC()
+        {
+            super(new CBCBlockCipher(new Shacal2Engine()), 256);//block size
+        }
+    }
+
+    public static class KeyGen
+        extends BaseKeyGenerator
+    {
+        public KeyGen()
+        {
+            super("Shacal2", 512, new CipherKeyGenerator());//key size
+        }
+    }
+
+    public static class AlgParamGen
+        extends BaseAlgorithmParameterGenerator
+    {
+        protected void engineInit(
+            AlgorithmParameterSpec genParamSpec,
+            SecureRandom random)
+            throws InvalidAlgorithmParameterException
+        {
+            throw new InvalidAlgorithmParameterException("No supported AlgorithmParameterSpec for Shacal2 parameter generation.");
+        }
+
+        protected AlgorithmParameters engineGenerateParameters()
+        {
+            byte[] iv = new byte[32];// block size 256
+
+            if (random == null)
+            {
+                random = new SecureRandom();
+            }
+
+            random.nextBytes(iv);
+
+            AlgorithmParameters params;
+
+            try
+            {
+                params = AlgorithmParameters.getInstance("Shacal2", BouncyCastleProvider.PROVIDER_NAME);
+                params.init(new IvParameterSpec(iv));
+            }
+            catch (Exception e)
+            {
+                throw new RuntimeException(e.getMessage());
+            }
+            return params;
+        }
+    }
+
+    public static class AlgParams
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "Shacal2 IV";
+        }
+    }
+
+    public static class Mappings
+        extends SymmetricAlgorithmProvider
+    {
+        private static final String PREFIX = Shacal2.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("Cipher.Shacal2", PREFIX + "$ECB");
+            provider.addAlgorithm("KeyGenerator.Shacal2", PREFIX + "$KeyGen");        
+            provider.addAlgorithm("AlgorithmParameterGenerator.Shacal2", PREFIX + "$AlgParamGen");
+        	provider.addAlgorithm("AlgorithmParameters.Shacal2", PREFIX + "$AlgParams");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java
index 49656c2..c1b3d19 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/SymmetricAlgorithmProvider.java
@@ -18,4 +18,17 @@
         provider.addAlgorithm("KeyGenerator." + algorithm + "-GMAC", keyGeneratorClassName);
         provider.addAlgorithm("Alg.Alias.KeyGenerator." + algorithm + "GMAC",  algorithm + "-GMAC");
     }
+
+    protected void addPoly1305Algorithm(ConfigurableProvider provider,
+                                        String algorithm,
+                                        String algorithmClassName,
+                                        String keyGeneratorClassName)
+    {
+        provider.addAlgorithm("Mac.POLY1305-" + algorithm, algorithmClassName);
+        provider.addAlgorithm("Alg.Alias.Mac.POLY1305" + algorithm, "POLY1305-" + algorithm);
+
+        provider.addAlgorithm("KeyGenerator.POLY1305-" + algorithm, keyGeneratorClassName);
+        provider.addAlgorithm("Alg.Alias.KeyGenerator.POLY1305" + algorithm, "POLY1305-" + algorithm);
+    }
+
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java
new file mode 100644
index 0000000..2970de6
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Threefish.java
@@ -0,0 +1,120 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.ThreefishEngine;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseBlockCipher;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.IvAlgorithmParameters;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+
+public final class Threefish
+{
+    private Threefish()
+    {
+    }
+
+    public static class ECB_256
+        extends BaseBlockCipher
+    {
+        public ECB_256()
+        {
+            super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_256));
+        }
+    }
+
+    public static class ECB_512
+        extends BaseBlockCipher
+    {
+        public ECB_512()
+        {
+            super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_512));
+        }
+    }
+
+    public static class ECB_1024
+        extends BaseBlockCipher
+    {
+        public ECB_1024()
+        {
+            super(new ThreefishEngine(ThreefishEngine.BLOCKSIZE_1024));
+        }
+    }
+
+    public static class KeyGen_256
+        extends BaseKeyGenerator
+    {
+        public KeyGen_256()
+        {
+            super("Threefish-256", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class KeyGen_512
+        extends BaseKeyGenerator
+    {
+        public KeyGen_512()
+        {
+            super("Threefish-512", 512, new CipherKeyGenerator());
+        }
+    }
+
+    public static class KeyGen_1024
+        extends BaseKeyGenerator
+    {
+        public KeyGen_1024()
+        {
+            super("Threefish-1024", 1024, new CipherKeyGenerator());
+        }
+    }
+
+    public static class AlgParams_256
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "Threefish-256 IV";
+        }
+    }
+
+    public static class AlgParams_512
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "Threefish-512 IV";
+        }
+    }
+
+    public static class AlgParams_1024
+        extends IvAlgorithmParameters
+    {
+        protected String engineToString()
+        {
+            return "Threefish-1024 IV";
+        }
+    }
+
+    public static class Mappings
+        extends AlgorithmProvider
+    {
+        private static final String PREFIX = Threefish.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+            provider.addAlgorithm("Cipher.Threefish-256", PREFIX + "$ECB_256");
+            provider.addAlgorithm("Cipher.Threefish-512", PREFIX + "$ECB_512");
+            provider.addAlgorithm("Cipher.Threefish-1024", PREFIX + "$ECB_1024");
+            provider.addAlgorithm("KeyGenerator.Threefish-256", PREFIX + "$KeyGen_256");
+            provider.addAlgorithm("KeyGenerator.Threefish-512", PREFIX + "$KeyGen_512");
+            provider.addAlgorithm("KeyGenerator.Threefish-1024", PREFIX + "$KeyGen_1024");
+            provider.addAlgorithm("AlgorithmParameters.Threefish-256", PREFIX + "$AlgParams_256");
+            provider.addAlgorithm("AlgorithmParameters.Threefish-512", PREFIX + "$AlgParams_512");
+            provider.addAlgorithm("AlgorithmParameters.Threefish-1024", PREFIX + "$AlgParams_1024");
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java
index 67b9f66..4c3ab1c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/Twofish.java
@@ -3,6 +3,7 @@
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.engines.TwofishEngine;
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
 import org.bouncycastle.crypto.macs.GMac;
 import org.bouncycastle.crypto.modes.CBCBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
@@ -53,6 +54,24 @@
         }
     }
 
+    public static class Poly1305
+        extends BaseMac
+    {
+        public Poly1305()
+        {
+            super(new org.bouncycastle.crypto.macs.Poly1305(new TwofishEngine()));
+        }
+    }
+
+    public static class Poly1305KeyGen
+        extends BaseKeyGenerator
+    {
+        public Poly1305KeyGen()
+        {
+            super("Poly1305-Twofish", 256, new Poly1305KeyGenerator());
+        }
+    }
+
     /**
      * PBEWithSHAAndTwofish-CBC
      */
@@ -107,6 +126,7 @@
             provider.addAlgorithm("SecretKeyFactory.PBEWITHSHAANDTWOFISH-CBC", PREFIX + "$PBEWithSHAKeyFactory");
 
             addGMacAlgorithm(provider, "Twofish", PREFIX + "$GMAC", PREFIX + "$KeyGen");
+            addPoly1305Algorithm(provider, "Twofish", PREFIX + "$Poly1305", PREFIX + "$Poly1305KeyGen");
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java
new file mode 100644
index 0000000..5be0640
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/XSalsa20.java
@@ -0,0 +1,51 @@
+package org.bouncycastle.jcajce.provider.symmetric;
+
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.engines.XSalsa20Engine;
+import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseKeyGenerator;
+import org.bouncycastle.jcajce.provider.symmetric.util.BaseStreamCipher;
+import org.bouncycastle.jcajce.provider.util.AlgorithmProvider;
+
+public final class XSalsa20
+{
+    private XSalsa20()
+    {
+    }
+    
+    public static class Base
+        extends BaseStreamCipher
+    {
+        public Base()
+        {
+            super(new XSalsa20Engine(), 24);
+        }
+    }
+
+    public static class KeyGen
+        extends BaseKeyGenerator
+    {
+        public KeyGen()
+        {
+            super("XSalsa20", 256, new CipherKeyGenerator());
+        }
+    }
+
+    public static class Mappings
+        extends AlgorithmProvider
+    {
+        private static final String PREFIX = XSalsa20.class.getName();
+
+        public Mappings()
+        {
+        }
+
+        public void configure(ConfigurableProvider provider)
+        {
+
+            provider.addAlgorithm("Cipher.XSALSA20", PREFIX + "$Base");
+            provider.addAlgorithm("KeyGenerator.XSALSA20", PREFIX + "$KeyGen");
+
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
index 17b66a5..943fa18 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseBlockCipher.java
@@ -1,5 +1,7 @@
 package org.bouncycastle.jcajce.provider.symmetric.util;
 
+import java.lang.reflect.Method;
+import java.nio.ByteBuffer;
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
@@ -20,6 +22,7 @@
 import javax.crypto.spec.RC2ParameterSpec;
 import javax.crypto.spec.RC5ParameterSpec;
 
+import org.bouncycastle.asn1.cms.GCMParameters;
 import org.bouncycastle.crypto.BlockCipher;
 import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherParameters;
@@ -32,6 +35,7 @@
 import org.bouncycastle.crypto.modes.CFBBlockCipher;
 import org.bouncycastle.crypto.modes.CTSBlockCipher;
 import org.bouncycastle.crypto.modes.EAXBlockCipher;
+import org.bouncycastle.crypto.modes.GCFBBlockCipher;
 import org.bouncycastle.crypto.modes.GCMBlockCipher;
 import org.bouncycastle.crypto.modes.GOFBBlockCipher;
 import org.bouncycastle.crypto.modes.OCBBlockCipher;
@@ -46,21 +50,24 @@
 import org.bouncycastle.crypto.paddings.TBCPadding;
 import org.bouncycastle.crypto.paddings.X923Padding;
 import org.bouncycastle.crypto.paddings.ZeroBytePadding;
+import org.bouncycastle.crypto.params.AEADParameters;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
 import org.bouncycastle.crypto.params.ParametersWithRandom;
 import org.bouncycastle.crypto.params.ParametersWithSBox;
 import org.bouncycastle.crypto.params.RC2Parameters;
 import org.bouncycastle.crypto.params.RC5Parameters;
+import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.jce.spec.GOST28147ParameterSpec;
-import org.bouncycastle.jce.spec.RepeatedSecretKeySpec;
 import org.bouncycastle.util.Strings;
 
 public class BaseBlockCipher
     extends BaseWrapCipher
     implements PBE
 {
+    private static final Class gcmSpecClass = lookup("javax.crypto.spec.GCMParameterSpec");
+
     //
     // specs we can handle.
     //
@@ -70,13 +77,15 @@
                                         RC5ParameterSpec.class,
                                         IvParameterSpec.class,
                                         PBEParameterSpec.class,
-                                        GOST28147ParameterSpec.class
+                                        GOST28147ParameterSpec.class,
+                                        gcmSpecClass
                                     };
 
     private BlockCipher             baseEngine;
     private BlockCipherProvider     engineProvider;
     private GenericBlockCipher      cipher;
     private ParametersWithIV        ivParam;
+    private AEADParameters          aeadParams;
 
     private int                     ivLength = 0;
 
@@ -87,6 +96,20 @@
 
     private String                  modeName = null;
 
+    private static Class lookup(String className)
+    {
+        try
+        {
+            Class def = BaseBlockCipher.class.getClassLoader().loadClass(className);
+
+            return def;
+        }
+        catch (Exception e)
+        {
+            return null;
+        }
+    }
+
     protected BaseBlockCipher(
         BlockCipher engine)
     {
@@ -105,6 +128,14 @@
     }
 
     protected BaseBlockCipher(
+        AEADBlockCipher engine)
+    {
+        baseEngine = engine.getUnderlyingCipher();
+        ivLength = baseEngine.getBlockSize();
+        cipher = new AEADGenericBlockCipher(engine);
+    }
+
+    protected BaseBlockCipher(
         org.bouncycastle.crypto.BlockCipher engine,
         int ivLength)
     {
@@ -181,6 +212,18 @@
                     throw new RuntimeException(e.toString());
                 }
             }
+            else if (aeadParams != null)
+            {
+                try
+                {
+                    engineParams = AlgorithmParameters.getInstance("GCM", BouncyCastleProvider.PROVIDER_NAME);
+                    engineParams.init(new GCMParameters(aeadParams.getNonce(), aeadParams.getMacSize()).getEncoded());
+                }
+                catch (Exception e)
+                {
+                    throw new RuntimeException(e.toString());
+                }
+            }
         }
 
         return engineParams;
@@ -271,6 +314,12 @@
             cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
                         new GOFBBlockCipher(baseEngine)));
         }
+        else if (modeName.startsWith("GCFB"))
+        {
+            ivLength = baseEngine.getBlockSize();
+            cipher = new BufferedGenericBlockCipher(new BufferedBlockCipher(
+                        new GCFBBlockCipher(baseEngine)));
+        }
         else if (modeName.startsWith("CTS"))
         {
             ivLength = baseEngine.getBlockSize();
@@ -278,14 +327,15 @@
         }
         else if (modeName.startsWith("CCM"))
         {
-            ivLength = baseEngine.getBlockSize();
+            ivLength = 13; // CCM nonce 7..13 bytes
             cipher = new AEADGenericBlockCipher(new CCMBlockCipher(baseEngine));
         }
         else if (modeName.startsWith("OCB"))
         {
             if (engineProvider != null)
             {
-                ivLength = baseEngine.getBlockSize();
+                // Nonce restricted to max 120 bits over 128 bit block cipher since draft-irtf-cfrg-ocb-03
+                ivLength = 15;
                 cipher = new AEADGenericBlockCipher(new OCBBlockCipher(baseEngine, engineProvider.get()));
             }
             else
@@ -377,6 +427,7 @@
         this.pbeSpec = null;
         this.pbeAlgorithm = null;
         this.engineParams = null;
+        this.aeadParams = null;
 
         //
         // basic key check
@@ -419,6 +470,18 @@
 
                     param = new ParametersWithIV(param, iv.getIV());
                 }
+                else if (params instanceof GOST28147ParameterSpec)
+                {
+                    // need to pick up IV and SBox.
+                    GOST28147ParameterSpec    gost28147Param = (GOST28147ParameterSpec)params;
+
+                    param = new ParametersWithSBox(param, gost28147Param.getSbox());
+
+                    if (gost28147Param.getIV() != null && ivLength != 0)
+                    {
+                        param = new ParametersWithIV(param, gost28147Param.getIV());
+                    }
+                }
             }
             else if (params instanceof PBEParameterSpec)
             {
@@ -528,12 +591,38 @@
                 ivParam = (ParametersWithIV)param;
             }
         }
+        else if (gcmSpecClass != null && gcmSpecClass.isInstance(params))
+        {
+            if (!isAEADModeName(modeName) && !(cipher instanceof AEADGenericBlockCipher))
+            {
+                throw new InvalidAlgorithmParameterException("GCMParameterSpec can only be used with AEAD modes.");
+            }
+
+            try
+            {
+                Method tLen = gcmSpecClass.getDeclaredMethod("getTLen", new Class[0]);
+                Method iv= gcmSpecClass.getDeclaredMethod("getIV", new Class[0]);
+
+                if (key instanceof RepeatedSecretKeySpec)
+                {
+                    param = aeadParams = new AEADParameters(null, ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0]));
+                }
+                else
+                {
+                    param = aeadParams = new AEADParameters(new KeyParameter(key.getEncoded()), ((Integer)tLen.invoke(params, new Object[0])).intValue(), (byte[])iv.invoke(params, new Object[0]));
+                }
+            }
+            catch (Exception e)
+            {
+                throw new InvalidAlgorithmParameterException("Cannot process GCMParameterSpec.");
+            }
+        }
         else
         {
             throw new InvalidAlgorithmParameterException("unknown parameter type.");
         }
 
-        if ((ivLength != 0) && !(param instanceof ParametersWithIV))
+        if ((ivLength != 0) && !(param instanceof ParametersWithIV) && !(param instanceof AEADParameters))
         {
             SecureRandom    ivRandom = random;
 
@@ -596,6 +685,11 @@
         {
             for (int i = 0; i != availableSpecs.length; i++)
             {
+                if (availableSpecs[i] == null)
+                {
+                    continue;
+                }
+
                 try
                 {
                     paramSpec = params.getParameterSpec(availableSpecs[i]);
@@ -634,6 +728,18 @@
         }
     }
 
+    protected void engineUpdateAAD(byte[] input, int offset, int length)
+    {
+        cipher.updateAAD(input, offset, length);
+    }
+
+    protected void engineUpdateAAD(ByteBuffer bytebuffer)
+    {
+        int offset = bytebuffer.arrayOffset() + bytebuffer.position();
+        int length = bytebuffer.limit() - bytebuffer.position();
+        engineUpdateAAD(bytebuffer.array(), offset, length);
+    }
+
     protected byte[] engineUpdate(
         byte[]  input,
         int     inputOffset,
@@ -783,6 +889,8 @@
 
         public int getUpdateOutputSize(int len);
 
+        public void updateAAD(byte[] input, int offset, int length);
+
         public int processByte(byte in, byte[] out, int outOff)
             throws DataLengthException;
 
@@ -844,6 +952,11 @@
             return cipher.getUpdateOutputSize(len);
         }
 
+        public void updateAAD(byte[] input, int offset, int length)
+        {
+            throw new UnsupportedOperationException("AAD is not supported in the current mode.");
+        }
+
         public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
         {
             return cipher.processByte(in, out, outOff);
@@ -901,6 +1014,11 @@
             return cipher.getUpdateOutputSize(len);
         }
 
+        public void updateAAD(byte[] input, int offset, int length)
+        {
+            cipher.processAADBytes(input, offset, length);
+        }
+
         public int processByte(byte in, byte[] out, int outOff) throws DataLengthException
         {
             return cipher.processByte(in, out, outOff);
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
index 442dcdd..270d648 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseMac.java
@@ -4,6 +4,9 @@
 import java.security.InvalidKeyException;
 import java.security.Key;
 import java.security.spec.AlgorithmParameterSpec;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
 
 import javax.crypto.MacSpi;
 import javax.crypto.spec.IvParameterSpec;
@@ -13,6 +16,8 @@
 import org.bouncycastle.crypto.Mac;
 import org.bouncycastle.crypto.params.KeyParameter;
 import org.bouncycastle.crypto.params.ParametersWithIV;
+import org.bouncycastle.crypto.params.SkeinParameters;
+import org.bouncycastle.jcajce.spec.SkeinParameterSpec;
 
 public class BaseMac
     extends MacSpi implements PBE
@@ -74,6 +79,10 @@
         {
             param = new ParametersWithIV(new KeyParameter(key.getEncoded()), ((IvParameterSpec)params).getIV());
         }
+        else if (params instanceof SkeinParameterSpec)
+        {
+            param = new SkeinParameters.Builder(copyMap(((SkeinParameterSpec)params).getParameters())).setKey(key.getEncoded()).build();
+        }
         else if (params == null)
         {
             param = new KeyParameter(key.getEncoded());
@@ -118,4 +127,18 @@
 
         return out;
     }
+
+    private static Hashtable copyMap(Map paramsMap)
+    {
+        Hashtable newTable = new Hashtable();
+
+        Iterator keys = paramsMap.keySet().iterator();
+        while (keys.hasNext())
+        {
+            Object key = keys.next();
+            newTable.put(key, paramsMap.get(key));
+        }
+
+        return newTable;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
index 6feab0e..31ba38f 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/BaseStreamCipher.java
@@ -3,6 +3,7 @@
 import java.security.AlgorithmParameters;
 import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
+import java.security.InvalidParameterException;
 import java.security.Key;
 import java.security.SecureRandom;
 import java.security.spec.AlgorithmParameterSpec;
@@ -201,7 +202,7 @@
         }
         else
         {
-            throw new IllegalArgumentException("unknown parameter type.");
+            throw new InvalidAlgorithmParameterException("unknown parameter type.");
         }
 
         if ((ivLength != 0) && !(param instanceof ParametersWithIV))
@@ -227,18 +228,25 @@
             }
         }
 
-        switch (opmode)
+        try
         {
-        case Cipher.ENCRYPT_MODE:
-        case Cipher.WRAP_MODE:
-            cipher.init(true, param);
-            break;
-        case Cipher.DECRYPT_MODE:
-        case Cipher.UNWRAP_MODE:
-            cipher.init(false, param);
-            break;
-        default:
-            System.out.println("eeek!");
+            switch (opmode)
+            {
+            case Cipher.ENCRYPT_MODE:
+            case Cipher.WRAP_MODE:
+                cipher.init(true, param);
+                break;
+            case Cipher.DECRYPT_MODE:
+            case Cipher.UNWRAP_MODE:
+                cipher.init(false, param);
+                break;
+            default:
+                throw new InvalidParameterException("unknown opmode " + opmode + " passed");
+            }
+        }
+        catch (Exception e)
+        {
+            throw new InvalidKeyException(e.getMessage());
         }
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
index f16de3c..fac3ead 100644
--- a/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/provider/symmetric/util/PBE.java
@@ -72,7 +72,32 @@
             }
             else if (type == PKCS5S2 || type == PKCS5S2_UTF8)
             {
-                generator = new PKCS5S2ParametersGenerator();
+                switch (hash)
+                {
+                case MD2:
+                    generator = new PKCS5S2ParametersGenerator(new MD2Digest());
+                    break;
+                case MD5:
+                    generator = new PKCS5S2ParametersGenerator(new MD5Digest());
+                    break;
+                case SHA1:
+                    generator = new PKCS5S2ParametersGenerator(new SHA1Digest());
+                    break;
+                case RIPEMD160:
+                    generator = new PKCS5S2ParametersGenerator(new RIPEMD160Digest());
+                    break;
+                case TIGER:
+                    generator = new PKCS5S2ParametersGenerator(new TigerDigest());
+                    break;
+                case SHA256:
+                    generator = new PKCS5S2ParametersGenerator(new SHA256Digest());
+                    break;
+                case GOST3411:
+                    generator = new PKCS5S2ParametersGenerator(new GOST3411Digest());
+                    break;
+                default:
+                    throw new IllegalStateException("unknown digest scheme for PBE PKCS5S2 encryption.");
+                }
             }
             else if (type == PKCS12)
             {
@@ -261,9 +286,9 @@
             key = convertPassword(type, keySpec);
             
             generator.init(key, keySpec.getSalt(), keySpec.getIterationCount());
-    
+
             param = generator.generateDerivedMacParameters(keySize);
-    
+
             for (int i = 0; i != key.length; i++)
             {
                 key[i] = 0;
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java
new file mode 100644
index 0000000..be341c4
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/GOST28147ParameterSpec.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.security.spec.AlgorithmParameterSpec;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.crypto.engines.GOST28147Engine;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A parameter spec for the GOST-28147 cipher.
+ */
+public class GOST28147ParameterSpec
+    implements AlgorithmParameterSpec
+{
+    private byte[] iv = null;
+    private byte[] sBox = null;
+
+    public GOST28147ParameterSpec(
+        byte[] sBox)
+    {
+        this.sBox = new byte[sBox.length];
+        
+        System.arraycopy(sBox, 0, this.sBox, 0, sBox.length);
+    }
+
+    public GOST28147ParameterSpec(
+        byte[] sBox,
+        byte[] iv)
+    {
+        this(sBox);
+        this.iv = new byte[iv.length];
+        
+        System.arraycopy(iv, 0, this.iv, 0, iv.length);
+    }
+    
+    public GOST28147ParameterSpec(
+        String sBoxName)
+    {
+        this.sBox = GOST28147Engine.getSBox(sBoxName);
+    }
+
+    public GOST28147ParameterSpec(
+        String sBoxName,
+        byte[] iv)
+    {
+        this(sBoxName);
+        this.iv = new byte[iv.length];
+        
+        System.arraycopy(iv, 0, this.iv, 0, iv.length);
+    }
+
+    public GOST28147ParameterSpec(
+        ASN1ObjectIdentifier sBoxName,
+        byte[] iv)
+    {
+        this(getName(sBoxName));
+        this.iv = Arrays.clone(iv);
+    }
+
+    public byte[] getSbox()
+    {
+        return sBox;
+    }
+
+    /**
+     * Returns the IV or null if this parameter set does not contain an IV.
+     *
+     * @return the IV or null if this parameter set does not contain an IV.
+     */
+    public byte[] getIV()
+    {
+        if (iv == null)
+        {
+            return null;
+        }
+
+        byte[]  tmp = new byte[iv.length];
+
+        System.arraycopy(iv, 0, tmp, 0, tmp.length);
+
+        return tmp;
+    }
+
+    private static Map oidMappings = new HashMap();
+
+    static
+    {
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, "E-A");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_B_ParamSet, "E-B");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_C_ParamSet, "E-C");
+        oidMappings.put(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_D_ParamSet, "E-D");
+    }
+
+    private static String getName(ASN1ObjectIdentifier sBoxOid)
+    {
+        String sBoxName = (String)oidMappings.get(sBoxOid);
+
+        if (sBoxName == null)
+        {
+            throw new IllegalArgumentException("unknown OID: " + sBoxOid);
+        }
+
+        return sBoxName;
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/PBKDF2KeySpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/PBKDF2KeySpec.java
new file mode 100644
index 0000000..214a5eb
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/PBKDF2KeySpec.java
@@ -0,0 +1,23 @@
+package org.bouncycastle.jcajce.spec;
+
+import javax.crypto.spec.PBEKeySpec;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+
+public class PBKDF2KeySpec
+    extends PBEKeySpec
+{
+    private AlgorithmIdentifier prf;
+
+    public PBKDF2KeySpec(char[] password, byte[] salt, int iterationCount, int keySize, AlgorithmIdentifier prf)
+    {
+        super(password, salt, iterationCount, keySize);
+
+        this.prf = prf;
+    }
+
+    public AlgorithmIdentifier getPrf()
+    {
+        return prf;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RepeatedSecretKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RepeatedSecretKeySpec.java
new file mode 100644
index 0000000..6af15db
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/RepeatedSecretKeySpec.java
@@ -0,0 +1,34 @@
+package org.bouncycastle.jcajce.spec;
+
+
+import javax.crypto.SecretKey;
+
+/**
+ * A simple object to indicate that a symmetric cipher should reuse the
+ * last key provided.
+ */
+public class RepeatedSecretKeySpec
+    implements SecretKey
+{
+    private String algorithm;
+
+    public RepeatedSecretKeySpec(String algorithm)
+    {
+        this.algorithm = algorithm;
+    }
+
+    public String getAlgorithm()
+    {
+        return algorithm;
+    }
+
+    public String getFormat()
+    {
+        return null;
+    }
+
+    public byte[] getEncoded()
+    {
+        return null;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java
new file mode 100644
index 0000000..b43aa95
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jcajce/spec/SkeinParameterSpec.java
@@ -0,0 +1,283 @@
+package org.bouncycastle.jcajce.spec;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStreamWriter;
+import java.security.spec.AlgorithmParameterSpec;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.Integers;
+
+/**
+ * Parameters for the Skein hash function - a series of byte[] strings identified by integer tags.
+ * <p/>
+ * Parameterised Skein can be used for:
+ * <ul>
+ * <li>MAC generation, by providing a {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setKey(byte[]) key}.</li>
+ * <li>Randomised hashing, by providing a {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setNonce(byte[]) nonce}.</li>
+ * <li>A hash function for digital signatures, associating a
+ * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPublicKey(byte[]) public key} with the message digest.</li>
+ * <li>A key derivation function, by providing a
+ * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setKeyIdentifier(byte[]) key identifier}.</li>
+ * <li>Personalised hashing, by providing a
+ * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPersonalisation(java.util.Date, String, String) recommended format} or
+ * {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec.Builder#setPersonalisation(byte[]) arbitrary} personalisation string.</li>
+ * </ul>
+ *
+ * @see org.bouncycastle.crypto.digests.SkeinEngine
+ * @see org.bouncycastle.crypto.digests.SkeinDigest
+ * @see org.bouncycastle.crypto.macs.SkeinMac
+ */
+public class SkeinParameterSpec
+    implements AlgorithmParameterSpec
+{
+    /**
+     * The parameter type for a secret key, supporting MAC or KDF functions: {@value
+     * #PARAM_TYPE_KEY}.
+     */
+    public static final int PARAM_TYPE_KEY = 0;
+
+    /**
+     * The parameter type for the Skein configuration block: {@value #PARAM_TYPE_CONFIG}.
+     */
+    public static final int PARAM_TYPE_CONFIG = 4;
+
+    /**
+     * The parameter type for a personalisation string: {@value #PARAM_TYPE_PERSONALISATION}.
+     */
+    public static final int PARAM_TYPE_PERSONALISATION = 8;
+
+    /**
+     * The parameter type for a public key: {@value #PARAM_TYPE_PUBLIC_KEY}.
+     */
+    public static final int PARAM_TYPE_PUBLIC_KEY = 12;
+
+    /**
+     * The parameter type for a key identifier string: {@value #PARAM_TYPE_KEY_IDENTIFIER}.
+     */
+    public static final int PARAM_TYPE_KEY_IDENTIFIER = 16;
+
+    /**
+     * The parameter type for a nonce: {@value #PARAM_TYPE_NONCE}.
+     */
+    public static final int PARAM_TYPE_NONCE = 20;
+
+    /**
+     * The parameter type for the message: {@value #PARAM_TYPE_MESSAGE}.
+     */
+    public static final int PARAM_TYPE_MESSAGE = 48;
+
+    /**
+     * The parameter type for the output transformation: {@value #PARAM_TYPE_OUTPUT}.
+     */
+    public static final int PARAM_TYPE_OUTPUT = 63;
+
+    private Map parameters;
+
+    public SkeinParameterSpec()
+    {
+        this(new HashMap());
+    }
+
+    private SkeinParameterSpec(Map parameters)
+    {
+        this.parameters = Collections.unmodifiableMap(parameters);
+    }
+
+    /**
+     * Obtains a map of type (Integer) to value (byte[]) for the parameters tracked in this object.
+     */
+    public Map getParameters()
+    {
+        return parameters;
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_KEY key parameter}, or <code>null</code> if not
+     * set.
+     */
+    public byte[] getKey()
+    {
+        return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY)));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_PERSONALISATION personalisation parameter}, or
+     * <code>null</code> if not set.
+     */
+    public byte[] getPersonalisation()
+    {
+        return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PERSONALISATION)));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_PUBLIC_KEY public key parameter}, or
+     * <code>null</code> if not set.
+     */
+    public byte[] getPublicKey()
+    {
+        return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_PUBLIC_KEY)));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_KEY_IDENTIFIER key identifier parameter}, or
+     * <code>null</code> if not set.
+     */
+    public byte[] getKeyIdentifier()
+    {
+        return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_KEY_IDENTIFIER)));
+    }
+
+    /**
+     * Obtains the value of the {@link #PARAM_TYPE_NONCE nonce parameter}, or <code>null</code> if
+     * not set.
+     */
+    public byte[] getNonce()
+    {
+        return Arrays.clone((byte[])parameters.get(Integers.valueOf(PARAM_TYPE_NONCE)));
+    }
+
+    /**
+     * A builder for {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec}.
+     */
+    public static class Builder
+    {
+        private Map parameters = new HashMap();
+
+        public Builder()
+        {
+        }
+
+        public Builder(SkeinParameterSpec params)
+        {
+            Iterator keys = params.parameters.keySet().iterator();
+            while (keys.hasNext())
+            {
+                Integer key = (Integer)keys.next();
+                parameters.put(key, params.parameters.get(key));
+            }
+        }
+
+        /**
+         * Sets a parameters to apply to the Skein hash function.<br>
+         * Parameter types must be in the range 0,5..62, and cannot use the value {@value
+         * org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} (reserved for message body).
+         * <p/>
+         * Parameters with type < {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE} are processed before
+         * the message content, parameters with type > {@value org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_MESSAGE}
+         * are processed after the message and prior to output.
+         *
+         * @param type  the type of the parameter, in the range 5..62.
+         * @param value the byte sequence of the parameter.
+         * @return
+         */
+        public Builder set(int type, byte[] value)
+        {
+            if (value == null)
+            {
+                throw new IllegalArgumentException("Parameter value must not be null.");
+            }
+            if ((type != PARAM_TYPE_KEY)
+                && (type <= PARAM_TYPE_CONFIG || type >= PARAM_TYPE_OUTPUT || type == PARAM_TYPE_MESSAGE))
+            {
+                throw new IllegalArgumentException("Parameter types must be in the range 0,5..47,49..62.");
+            }
+            if (type == PARAM_TYPE_CONFIG)
+            {
+                throw new IllegalArgumentException("Parameter type " + PARAM_TYPE_CONFIG
+                    + " is reserved for internal use.");
+            }
+            this.parameters.put(Integers.valueOf(type), value);
+            return this;
+        }
+
+        /**
+         * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_KEY} parameter.
+         */
+        public Builder setKey(byte[] key)
+        {
+            return set(PARAM_TYPE_KEY, key);
+        }
+
+        /**
+         * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_PERSONALISATION} parameter.
+         */
+        public Builder setPersonalisation(byte[] personalisation)
+        {
+            return set(PARAM_TYPE_PERSONALISATION, personalisation);
+        }
+
+        /**
+         * Implements the recommended personalisation format for Skein defined in Section 4.11 of
+         * the Skein 1.3 specification.
+         * <p/>
+         * The format is <code>YYYYMMDD email@address distinguisher</code>, encoded to a byte
+         * sequence using UTF-8 encoding.
+         *
+         * @param date          the date the personalised application of the Skein was defined.
+         * @param emailAddress  the email address of the creation of the personalised application.
+         * @param distinguisher an arbitrary personalisation string distinguishing the application.
+         * @return
+         */
+        public Builder setPersonalisation(Date date, String emailAddress, String distinguisher)
+        {
+            try
+            {
+                final ByteArrayOutputStream bout = new ByteArrayOutputStream();
+                final OutputStreamWriter out = new OutputStreamWriter(bout, "UTF-8");
+                final DateFormat format = new SimpleDateFormat("YYYYMMDD");
+                out.write(format.format(date));
+                out.write(" ");
+                out.write(emailAddress);
+                out.write(" ");
+                out.write(distinguisher);
+                out.close();
+                return set(PARAM_TYPE_PERSONALISATION, bout.toByteArray());
+            }
+            catch (IOException e)
+            {
+                throw new IllegalStateException("Byte I/O failed: " + e);
+            }
+        }
+
+        /**
+         * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+         */
+        public Builder setPublicKey(byte[] publicKey)
+        {
+            return set(PARAM_TYPE_PUBLIC_KEY, publicKey);
+        }
+
+        /**
+         * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_KEY_IDENTIFIER} parameter.
+         */
+        public Builder setKeyIdentifier(byte[] keyIdentifier)
+        {
+            return set(PARAM_TYPE_KEY_IDENTIFIER, keyIdentifier);
+        }
+
+        /**
+         * Sets the {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec#PARAM_TYPE_NONCE} parameter.
+         */
+        public Builder setNonce(byte[] nonce)
+        {
+            return set(PARAM_TYPE_NONCE, nonce);
+        }
+
+        /**
+         * Constructs a new {@link org.bouncycastle.jcajce.spec.SkeinParameterSpec} instance with the parameters provided to this
+         * builder.
+         */
+        public SkeinParameterSpec build()
+        {
+            return new SkeinParameterSpec(parameters);
+        }
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java b/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java
index cab5a45..941f476 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/ECNamedCurveTable.java
@@ -1,13 +1,8 @@
 package org.bouncycastle.jce;
 
 import java.util.Enumeration;
-import java.util.Vector;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
-import org.bouncycastle.asn1.nist.NISTNamedCurves;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.teletrust.TeleTrusTNamedCurves;
-import org.bouncycastle.asn1.x9.X962NamedCurves;
 import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec;
 
@@ -26,12 +21,12 @@
     public static ECNamedCurveParameterSpec getParameterSpec(
         String  name)
     {
-        X9ECParameters  ecP = X962NamedCurves.getByName(name);
+        X9ECParameters  ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName(name);
         if (ecP == null)
         {
             try
             {
-                ecP = X962NamedCurves.getByOID(new ASN1ObjectIdentifier(name));
+                ecP = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByOID(new ASN1ObjectIdentifier(name));
             }
             catch (IllegalArgumentException e)
             {
@@ -41,43 +36,6 @@
         
         if (ecP == null)
         {
-            ecP = SECNamedCurves.getByName(name);
-            if (ecP == null)
-            {
-                try
-                {
-                    ecP = SECNamedCurves.getByOID(new ASN1ObjectIdentifier(name));
-                }
-                catch (IllegalArgumentException e)
-                {
-                    // ignore - not an oid
-                }
-            }
-        }
-
-        if (ecP == null)
-        {
-            ecP = TeleTrusTNamedCurves.getByName(name);
-            if (ecP == null)
-            {
-                try
-                {
-                    ecP = TeleTrusTNamedCurves.getByOID(new ASN1ObjectIdentifier(name));
-                }
-                catch (IllegalArgumentException e)
-                {
-                    // ignore - not an oid
-                }
-            }
-        }
-
-        if (ecP == null)
-        {
-            ecP = NISTNamedCurves.getByName(name);
-        }
-        
-        if (ecP == null)
-        {
             return null;
         }
 
@@ -97,23 +55,6 @@
      */
     public static Enumeration getNames()
     {
-        Vector v = new Vector();
-        
-        addEnumeration(v, X962NamedCurves.getNames());
-        addEnumeration(v, SECNamedCurves.getNames());
-        addEnumeration(v, NISTNamedCurves.getNames());
-        addEnumeration(v, TeleTrusTNamedCurves.getNames());
-
-        return v.elements();
-    }
-
-    private static void addEnumeration(
-        Vector v, 
-        Enumeration e)
-    {
-        while (e.hasMoreElements())
-        {
-            v.addElement(e.nextElement());
-        }
+        return org.bouncycastle.asn1.x9.ECNamedCurveTable.getNames();
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java b/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java
index 3518583..5ff966a 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/ECPointUtil.java
@@ -50,7 +50,7 @@
         }
         
         org.bouncycastle.math.ec.ECPoint p = c.decodePoint(encoded);
-        
-        return new ECPoint(p.getX().toBigInteger(), p.getY().toBigInteger());
+
+        return new ECPoint(p.getAffineXCoord().toBigInteger(), p.getAffineYCoord().toBigInteger());
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java b/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java
index efa0f66..ddd38e8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/X509Principal.java
@@ -19,6 +19,7 @@
  * PrincipalUtil class.
  * </p>
  * @see org.bouncycastle.jce.PrincipalUtil
+ * @deprecated use the X500Name class.
  */
 public class X509Principal
     extends X509Name
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/examples/package.html b/bcprov/src/main/java/org/bouncycastle/jce/examples/package.html
deleted file mode 100644
index 96b3193..0000000
--- a/bcprov/src/main/java/org/bouncycastle/jce/examples/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Example classes for use with the JCE.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/package.html b/bcprov/src/main/java/org/bouncycastle/jce/interfaces/package.html
deleted file mode 100644
index bacde6c..0000000
--- a/bcprov/src/main/java/org/bouncycastle/jce/interfaces/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Interfaces for supporting Elliptic Curve Keys, El Gamal, and PKCS12 attributes.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/package.html b/bcprov/src/main/java/org/bouncycastle/jce/package.html
deleted file mode 100644
index 52ef3bf..0000000
--- a/bcprov/src/main/java/org/bouncycastle/jce/package.html
+++ /dev/null
@@ -1,10 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Utility classes for use with the JCE.
-<p>
-The classes in this package support the generation of certificates and PKCS10 signing requests.
-<p>
-Note: the PKCS7 class is deprecated, for a fuller version of CMS see the cms package distributed
-with the BC mail API.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
index 0433965..dc7db18 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/BouncyCastleProvider.java
@@ -44,7 +44,7 @@
 public final class BouncyCastleProvider extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Security Provider v1.49";
+    private static String info = "BouncyCastle Security Provider v1.50";
 
     public static final String PROVIDER_NAME = "BC";
 
@@ -69,8 +69,10 @@
 
     private static final String[] SYMMETRIC_CIPHERS =
     {
-        "AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "DES", "DESede", "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA",
-        "Noekeon", "RC2", "RC5", "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Skipjack", "TEA", "Twofish", "VMPC", "VMPCKSA3", "XTEA"
+        "AES", "ARC4", "Blowfish", "Camellia", "CAST5", "CAST6", "ChaCha", "DES", "DESede",
+        "GOST28147", "Grainv1", "Grain128", "HC128", "HC256", "IDEA", "Noekeon", "RC2", "RC5",
+        "RC6", "Rijndael", "Salsa20", "SEED", "Serpent", "Shacal2", "Skipjack", "TEA", "Twofish", "Threefish",
+        "VMPC", "VMPCKSA3", "XTEA", "XSalsa20"
     };
 
      /*
@@ -96,11 +98,11 @@
     private static final String DIGEST_PACKAGE = "org.bouncycastle.jcajce.provider.digest.";
     private static final String[] DIGESTS =
     {
-        "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Tiger", "Whirlpool"
+        "GOST3411", "MD2", "MD4", "MD5", "SHA1", "RIPEMD128", "RIPEMD160", "RIPEMD256", "RIPEMD320", "SHA224", "SHA256", "SHA384", "SHA512", "SHA3", "Skein", "SM3", "Tiger", "Whirlpool"
     };
 
     /*
-     * Configurable digests
+     * Configurable keystores
      */
     private static final String KEYSTORE_PACKAGE = "org.bouncycastle.jcajce.provider.keystore.";
     private static final String[] KEYSTORES =
@@ -115,7 +117,7 @@
      */
     public BouncyCastleProvider()
     {
-        super(PROVIDER_NAME, 1.49, info);
+        super(PROVIDER_NAME, 1.50, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
index 3175237..63bb6d8 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPrivateKey.java
@@ -125,8 +125,8 @@
             this.ecSpec = new ECParameterSpec(
                             ellipticCurve,
                             new ECPoint(
-                                    dp.getG().getX().toBigInteger(),
-                                    dp.getG().getY().toBigInteger()),
+                                    dp.getG().getAffineXCoord().toBigInteger(),
+                                    dp.getG().getAffineYCoord().toBigInteger()),
                             dp.getN(),
                             dp.getH().intValue());
         }
@@ -156,8 +156,8 @@
             this.ecSpec = new ECParameterSpec(
                             ellipticCurve,
                             new ECPoint(
-                                    dp.getG().getX().toBigInteger(),
-                                    dp.getG().getY().toBigInteger()),
+                                    dp.getG().getAffineXCoord().toBigInteger(),
+                                    dp.getG().getAffineYCoord().toBigInteger()),
                             dp.getN(),
                             dp.getH().intValue());
         }
@@ -168,8 +168,8 @@
             this.ecSpec = new ECParameterSpec(
                                 ellipticCurve,
                                 new ECPoint(
-                                        spec.getG().getX().toBigInteger(),
-                                        spec.getG().getY().toBigInteger()),
+                                    spec.getG().getAffineXCoord().toBigInteger(),
+                                    spec.getG().getAffineYCoord().toBigInteger()),
                                 spec.getN(),
                                 spec.getH().intValue());
         }
@@ -212,8 +212,8 @@
                         ECGOST3410NamedCurves.getName(oid),
                         ellipticCurve,
                         new ECPoint(
-                                gParam.getG().getX().toBigInteger(),
-                                gParam.getG().getY().toBigInteger()),
+                                gParam.getG().getAffineXCoord().toBigInteger(),
+                                gParam.getG().getAffineYCoord().toBigInteger()),
                         gParam.getN(),
                         gParam.getH());
             }
@@ -225,8 +225,8 @@
                         ECUtil.getCurveName(oid),
                         ellipticCurve,
                         new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
+                                ecP.getG().getAffineXCoord().toBigInteger(),
+                                ecP.getG().getAffineYCoord().toBigInteger()),
                         ecP.getN(),
                         ecP.getH());
             }
@@ -243,8 +243,8 @@
             this.ecSpec = new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                        ecP.getG().getX().toBigInteger(),
-                        ecP.getG().getY().toBigInteger()),
+                        ecP.getG().getAffineXCoord().toBigInteger(),
+                        ecP.getG().getAffineYCoord().toBigInteger()),
                 ecP.getN(),
                 ecP.getH().intValue());
         }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
index 00df81f..4bf2e68 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/JCEECPublicKey.java
@@ -88,7 +88,7 @@
             {
                 org.bouncycastle.jce.spec.ECParameterSpec s = BouncyCastleProvider.CONFIGURATION.getEcImplicitlyCa();
 
-                q = s.getCurve().createPoint(q.getX().toBigInteger(), q.getY().toBigInteger(), false);
+                q = s.getCurve().createPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger(), false);
             }               
             this.ecSpec = null;
         }
@@ -157,8 +157,8 @@
         return new ECParameterSpec(
                 ellipticCurve,
                 new ECPoint(
-                        dp.getG().getX().toBigInteger(),
-                        dp.getG().getY().toBigInteger()),
+                        dp.getG().getAffineXCoord().toBigInteger(),
+                        dp.getG().getAffineYCoord().toBigInteger()),
                         dp.getN(),
                         dp.getH().intValue());
     }
@@ -221,8 +221,8 @@
                     ECGOST3410NamedCurves.getName(gostParams.getPublicKeyParamSet()),
                     ellipticCurve,
                     new ECPoint(
-                            spec.getG().getX().toBigInteger(),
-                            spec.getG().getY().toBigInteger()),
+                            spec.getG().getAffineXCoord().toBigInteger(),
+                            spec.getG().getAffineYCoord().toBigInteger()),
                             spec.getN(), spec.getH());
 
         }
@@ -244,8 +244,8 @@
                         ECUtil.getCurveName(oid),
                         ellipticCurve,
                         new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
+                                ecP.getG().getAffineXCoord().toBigInteger(),
+                                ecP.getG().getAffineYCoord().toBigInteger()),
                         ecP.getN(),
                         ecP.getH());
             }
@@ -264,8 +264,8 @@
                 this.ecSpec = new ECParameterSpec(
                         ellipticCurve,
                         new ECPoint(
-                                ecP.getG().getX().toBigInteger(),
-                                ecP.getG().getY().toBigInteger()),
+                                ecP.getG().getAffineXCoord().toBigInteger(),
+                                ecP.getG().getAffineYCoord().toBigInteger()),
                         ecP.getN(),
                         ecP.getH().intValue());
             }
@@ -344,8 +344,8 @@
                 }
             }
 
-            BigInteger      bX = this.q.getX().toBigInteger();
-            BigInteger      bY = this.q.getY().toBigInteger();
+            BigInteger      bX = this.q.getAffineXCoord().toBigInteger();
+            BigInteger      bY = this.q.getAffineYCoord().toBigInteger();
             byte[]          encKey = new byte[64];
 
             extractBytes(encKey, 0, bX);
@@ -391,7 +391,7 @@
 
             ECCurve curve = this.engineGetQ().getCurve();
             ASN1OctetString p = (ASN1OctetString)
-                new X9ECPoint(curve.createPoint(this.getQ().getX().toBigInteger(), this.getQ().getY().toBigInteger(), withCompression)).toASN1Primitive();
+                new X9ECPoint(curve.createPoint(this.getQ().getAffineXCoord().toBigInteger(), this.getQ().getAffineYCoord().toBigInteger(), withCompression)).toASN1Primitive();
 
             info = new SubjectPublicKeyInfo(new AlgorithmIdentifier(X9ObjectIdentifiers.id_ecPublicKey, params), p.getOctets());
         }
@@ -432,7 +432,7 @@
 
     public ECPoint getW()
     {
-        return new ECPoint(q.getX().toBigInteger(), q.getY().toBigInteger());
+        return new ECPoint(q.getAffineXCoord().toBigInteger(), q.getAffineYCoord().toBigInteger());
     }
 
     public org.bouncycastle.math.ec.ECPoint getQ()
@@ -441,11 +441,11 @@
         {
             if (q instanceof org.bouncycastle.math.ec.ECPoint.Fp)
             {
-                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.Fp(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
             else
             {
-                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getX(), q.getY());
+                return new org.bouncycastle.math.ec.ECPoint.F2m(null, q.getAffineXCoord(), q.getAffineYCoord());
             }
         }
 
@@ -473,8 +473,8 @@
         String          nl = System.getProperty("line.separator");
 
         buf.append("EC Public Key").append(nl);
-        buf.append("            X: ").append(this.q.getX().toBigInteger().toString(16)).append(nl);
-        buf.append("            Y: ").append(this.q.getY().toBigInteger().toString(16)).append(nl);
+        buf.append("            X: ").append(this.q.getAffineXCoord().toBigInteger().toString(16)).append(nl);
+        buf.append("            Y: ").append(this.q.getAffineYCoord().toBigInteger().toString(16)).append(nl);
 
         return buf.toString();
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
index d5c3700..7e76a89 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLEntryObject.java
@@ -211,6 +211,23 @@
         return hashValue;
     }
 
+    public boolean equals(Object o)
+    {
+        if (o == this)
+        {
+            return true;
+        }
+
+        if (o instanceof X509CRLEntryObject)
+        {
+            X509CRLEntryObject other = (X509CRLEntryObject)o;
+
+            return this.c.equals(other.c);
+        }
+
+        return super.equals(this);
+    }
+
     public byte[] getEncoded()
         throws CRLException
     {
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
index cd83211..b5b4f13 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/X509CRLObject.java
@@ -59,6 +59,8 @@
     private String sigAlgName;
     private byte[] sigAlgParams;
     private boolean isIndirect;
+    private boolean isHashCodeSet = false;
+    private int     hashCodeValue;
 
     static boolean isIndirectCRL(X509CRL crl)
         throws CRLException
@@ -520,7 +522,7 @@
             throw new RuntimeException("X.509 CRL used with non X.509 Cert");
         }
 
-        TBSCertList.CRLEntry[] certs = c.getRevokedCertificates();
+        Enumeration certs = c.getRevokedCertificateEnumeration();
 
         X500Name caName = c.getIssuer();
 
@@ -528,11 +530,13 @@
         {
             BigInteger serial = ((X509Certificate)cert).getSerialNumber();
 
-            for (int i = 0; i < certs.length; i++)
+            while (certs.hasMoreElements())
             {
-                if (isIndirect && certs[i].hasExtensions())
+                TBSCertList.CRLEntry entry = TBSCertList.CRLEntry.getInstance(certs.nextElement());
+
+                if (isIndirect && entry.hasExtensions())
                 {
-                    Extension currentCaName = certs[i].getExtensions().getExtension(Extension.certificateIssuer);
+                    Extension currentCaName = entry.getExtensions().getExtension(Extension.certificateIssuer);
 
                     if (currentCaName != null)
                     {
@@ -540,7 +544,7 @@
                     }
                 }
 
-                if (certs[i].getUserCertificate().getValue().equals(serial))
+                if (entry.getUserCertificate().getValue().equals(serial))
                 {
                     X500Name issuer;
 
@@ -572,5 +576,50 @@
 
         return false;
     }
+
+    public boolean equals(Object other)
+    {
+        if (this == other)
+        {
+            return true;
+        }
+
+        if (!(other instanceof X509CRL))
+        {
+            return false;
+        }
+
+        if (other instanceof X509CRLObject)
+        {
+            X509CRLObject crlObject = (X509CRLObject)other;
+
+            if (isHashCodeSet)
+            {
+                boolean otherIsHashCodeSet = crlObject.isHashCodeSet;
+                if (otherIsHashCodeSet)
+                {
+                    if (crlObject.hashCodeValue != hashCodeValue)
+                    {
+                        return false;
+                    }
+                }
+            }
+
+            return this.c.equals(crlObject.c);
+        }
+
+        return super.equals(other);
+    }
+
+    public int hashCode()
+    {
+        if (!isHashCodeSet)
+        {
+            isHashCodeSet = true;
+            hashCodeValue = super.hashCode();
+        }
+
+        return hashCodeValue;
+    }
 }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AEADTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AEADTest.java
new file mode 100644
index 0000000..d2f1405
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AEADTest.java
@@ -0,0 +1,241 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.IOException;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Security;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.asn1.cms.GCMParameters;
+import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class AEADTest extends SimpleTest
+{
+
+    // EAX test vector from EAXTest
+    private byte[] K2 = Hex.decode("91945D3F4DCBEE0BF45EF52255F095A4");
+    private byte[] N2 = Hex.decode("BECAF043B0A23D843194BA972C66DEBD");
+    private byte[] A2 = Hex.decode("FA3BFD4806EB53FA");
+    private byte[] P2 = Hex.decode("F7FB");
+    private byte[] C2 = Hex.decode("19DD5C4C9331049D0BDAB0277408F67967E5");
+    // C2 with only 64bit MAC (default for EAX)
+    private byte[] C2_short = Hex.decode("19DD5C4C9331049D0BDA");
+
+    private byte[] KGCM = Hex.decode("00000000000000000000000000000000");
+    private byte[] NGCM = Hex.decode("000000000000000000000000");
+    private byte[] CGCM = Hex.decode("58e2fccefa7e3061367f1d57a4e7455a");
+
+    public String getName()
+    {
+        return "AEAD";
+    }
+
+    public void performTest() throws Exception
+    {
+        try
+        {
+            this.getClass().getClassLoader().loadClass("javax.crypto.spec.GCMParameterSpec");
+
+            checkCipherWithAD(K2, N2, A2, P2, C2_short);
+            testGCMParameterSpec(K2, N2, A2, P2, C2);
+            testGCMParameterSpecWithRepeatKey(K2, N2, A2, P2, C2);
+            testGCMGeneric(KGCM, NGCM, new byte[0], new byte[0], CGCM);
+        }
+        catch (ClassNotFoundException e)
+        {
+            System.err.println("AEADTest disabled due to JDK");
+        }
+    }
+
+    private void checkCipherWithAD(byte[] K,
+                                   byte[] N,
+                                   byte[] A,
+                                   byte[] P,
+                                   byte[] C) throws InvalidKeyException,
+            NoSuchAlgorithmException, NoSuchPaddingException,
+            IllegalBlockSizeException, BadPaddingException,
+            InvalidAlgorithmParameterException, NoSuchProviderException
+    {
+        Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
+        SecretKeySpec key = new SecretKeySpec(K, "AES");
+        IvParameterSpec iv = new IvParameterSpec(N);
+        eax.init(Cipher.ENCRYPT_MODE, key, iv);
+
+        eax.updateAAD(A);
+        byte[] c = eax.doFinal(P);
+
+        if (!areEqual(C, c))
+        {
+            fail("JCE encrypt with additional data failed.");
+        }
+
+        eax.init(Cipher.DECRYPT_MODE, key, iv);
+        eax.updateAAD(A);
+        byte[] p = eax.doFinal(C);
+
+        if (!areEqual(P, p))
+        {
+            fail("JCE decrypt with additional data failed.");
+        }
+    }
+
+    private void testGCMParameterSpec(byte[] K,
+                                      byte[] N,
+                                      byte[] A,
+                                      byte[] P,
+                                      byte[] C)
+        throws InvalidKeyException,
+        NoSuchAlgorithmException, NoSuchPaddingException,
+        IllegalBlockSizeException, BadPaddingException,
+        InvalidAlgorithmParameterException, NoSuchProviderException, IOException
+    {
+        Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
+        SecretKeySpec key = new SecretKeySpec(K, "AES");
+
+        // GCMParameterSpec mapped to AEADParameters and overrides default MAC
+        // size
+        GCMParameterSpec spec = new GCMParameterSpec(128, N);
+        eax.init(Cipher.ENCRYPT_MODE, key, spec);
+
+        eax.updateAAD(A);
+        byte[] c = eax.doFinal(P);
+
+        if (!areEqual(C, c))
+        {
+            fail("JCE encrypt with additional data and GCMParameterSpec failed.");
+        }
+
+        eax.init(Cipher.DECRYPT_MODE, key, spec);
+        eax.updateAAD(A);
+        byte[] p = eax.doFinal(C);
+
+        if (!areEqual(P, p))
+        {
+            fail("JCE decrypt with additional data and GCMParameterSpec failed.");
+        }
+
+        AlgorithmParameters algParams = eax.getParameters();
+
+        byte[] encParams = algParams.getEncoded();
+
+        GCMParameters gcmParameters = GCMParameters.getInstance(encParams);
+
+        if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen())
+        {
+            fail("parameters mismatch");
+        }
+    }
+
+    private void testGCMParameterSpecWithRepeatKey(byte[] K,
+                                                   byte[] N,
+                                                   byte[] A,
+                                                   byte[] P,
+                                                   byte[] C)
+        throws InvalidKeyException, NoSuchAlgorithmException,
+        NoSuchPaddingException, IllegalBlockSizeException,
+        BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException, IOException
+    {
+        Cipher eax = Cipher.getInstance("AES/EAX/NoPadding", "BC");
+        SecretKeySpec key = new SecretKeySpec(K, "AES");
+        GCMParameterSpec spec = new GCMParameterSpec(128, N);
+        eax.init(Cipher.ENCRYPT_MODE, key, spec);
+
+        eax.updateAAD(A);
+        byte[] c = eax.doFinal(P);
+
+        if (!areEqual(C, c))
+        {
+            fail("JCE encrypt with additional data and RepeatedSecretKeySpec failed.");
+        }
+
+        // Check GCMParameterSpec handling knows about RepeatedSecretKeySpec
+        eax.init(Cipher.DECRYPT_MODE, new RepeatedSecretKeySpec("AES"), spec);
+        eax.updateAAD(A);
+        byte[] p = eax.doFinal(C);
+
+        if (!areEqual(P, p))
+        {
+            fail("JCE decrypt with additional data and RepeatedSecretKeySpec failed.");
+        }
+
+        AlgorithmParameters algParams = eax.getParameters();
+
+        byte[] encParams = algParams.getEncoded();
+
+        GCMParameters gcmParameters = GCMParameters.getInstance(encParams);
+
+        if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen())
+        {
+            fail("parameters mismatch");
+        }
+    }
+
+    private void testGCMGeneric(byte[] K,
+                                      byte[] N,
+                                      byte[] A,
+                                      byte[] P,
+                                      byte[] C)
+        throws InvalidKeyException,
+        NoSuchAlgorithmException, NoSuchPaddingException,
+        IllegalBlockSizeException, BadPaddingException,
+        InvalidAlgorithmParameterException, NoSuchProviderException, IOException
+    {
+        Cipher eax = Cipher.getInstance("AES/GCM/NoPadding", "BC");
+        SecretKeySpec key = new SecretKeySpec(K, "AES");
+
+        // GCMParameterSpec mapped to AEADParameters and overrides default MAC
+        // size
+        GCMParameterSpec spec = new GCMParameterSpec(128, N);
+        eax.init(Cipher.ENCRYPT_MODE, key, spec);
+
+        eax.updateAAD(A);
+        byte[] c = eax.doFinal(P);
+
+        if (!areEqual(C, c))
+        {
+            fail("JCE encrypt with additional data and GCMParameterSpec failed.");
+        }
+
+        eax = Cipher.getInstance("GCM", "BC");
+        eax.init(Cipher.DECRYPT_MODE, key, spec);
+        eax.updateAAD(A);
+        byte[] p = eax.doFinal(C);
+
+        if (!areEqual(P, p))
+        {
+            fail("JCE decrypt with additional data and GCMParameterSpec failed.");
+        }
+
+        AlgorithmParameters algParams = eax.getParameters();
+
+        byte[] encParams = algParams.getEncoded();
+
+        GCMParameters gcmParameters = GCMParameters.getInstance(encParams);
+
+        if (!Arrays.areEqual(spec.getIV(), gcmParameters.getNonce()) || spec.getTLen() != gcmParameters.getIcvLen())
+        {
+            fail("parameters mismatch");
+        }
+    }
+
+    public static void main(String[] args) throws Exception
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new AEADTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java
index 61f7995..ae6d7bc 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/AESSICTest.java
@@ -7,8 +7,8 @@
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
+import org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.jce.spec.RepeatedSecretKeySpec;
 import org.bouncycastle.util.encoders.Hex;
 import org.bouncycastle.util.test.SimpleTest;
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
index 30489bf..0c9cf01 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/BlockCipherTest.java
@@ -1,22 +1,5 @@
 package org.bouncycastle.jce.provider.test;
 
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Hex;
-import org.bouncycastle.util.test.SimpleTest;
-
-import javax.crypto.Cipher;
-import javax.crypto.CipherInputStream;
-import javax.crypto.CipherOutputStream;
-import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.KeyGenerator;
-import javax.crypto.SecretKey;
-import javax.crypto.SecretKeyFactory;
-import javax.crypto.ShortBufferException;
-import javax.crypto.spec.DESedeKeySpec;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.RC2ParameterSpec;
-import javax.crypto.spec.RC5ParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
@@ -33,6 +16,24 @@
 import java.security.spec.InvalidKeySpecException;
 import java.security.spec.KeySpec;
 
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.DESedeKeySpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.RC2ParameterSpec;
+import javax.crypto.spec.RC5ParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
 /**
  * basic test class for a block cipher, basically this just exercises the provider, and makes sure we
  * are behaving sensibly, correctness of the implementation is shown in the lightweight test classes.
@@ -204,6 +205,12 @@
         "6cd6f7c5d2c655556d7a9e98a1696d1875e9f1b2fc991e28a2d55b56861e80bd",
         "Twofish/OFB/NoPadding",
         "821c54b1b54ae113cf74595eefe10c83b61c9682fc81f92c52f39a3a693f88b8",
+        "Threefish-256/OFB/NoPadding",
+        "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373",
+        "Threefish-512/OFB/NoPadding",
+        "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2",
+        "Threefish-1024/OFB/NoPadding",
+        "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454",
         "RC2/OFB/NoPadding",
         "0a07cb78537cb04c0c74e28a7b86b80f80acadf87d6ef32792f1a8cf74b39f74",
         "RC5/OFB/NoPadding",
@@ -220,6 +227,12 @@
         "6ca6078755b263f09787d830b6fda7b7748494634bdc73ab68540cf9f6b7eccf",
         "Twofish/OFB8/NoPadding",
         "825dcec234ad52253d6e064b0d769bc04b1142435933f4a510ffc20d70095a88",
+        "Threefish-256/OFB8/NoPadding",
+        "545fbd92313512127218262dd4394569aca96ba122e1432b661ecfc01af3a25c",
+        "Threefish-512/OFB8/NoPadding",
+        "15f6e7d215662c525ea982cab56409cf833157e1af06edd57a13c71487904fea",
+        "Threefish-1024/OFB8/NoPadding",
+        "03d80b67ff7139d9dd8b07280642f94074496e5fc37b1ba1f8593cdf64a1e4ca",
         "RC2/OFB8/NoPadding",
         "0aa26c6f6a820fe7d38da97085995ad62e2e293323a76300fcd4eb572810f7c6",
         "RC5/OFB8/NoPadding",
@@ -236,6 +249,12 @@
         "6cd6f7c5d2c6555561167fe9b10665102206869339122f1ed89efa4a985397f6",
         "Twofish/CFB/NoPadding",
         "821c54b1b54ae113cf74595eefe10c8308b7a438277de4f40948ac2d172d53d2",
+        "Threefish-256/CFB/NoPadding",
+        "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373",
+        "Threefish-512/CFB/NoPadding",
+        "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2",
+        "Threefish-1024/CFB/NoPadding",
+        "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454",
         "RC2/CFB/NoPadding",
         "0a07cb78537cb04ca1401450d5cd411c7da7fa5b6baaa17bb2137bd95c9f26a5",
         "RC5/CFB/NoPadding",
@@ -252,6 +271,12 @@
         "6ca63aaada9188d2410c07513cc0736b9888770768c25a5befc776beea5bdc4c",
         "Twofish/CFB8/NoPadding",
         "825d12af040721cf5ed4a4798647837ac5eb14d752aace28728aeb37b2010abd",
+        "Threefish-256/CFB8/NoPadding",
+        "545fbf0a4b925f399cf7540f1cc1cc6012e329ab2d4db0aa0dfa29ee2a2019d1",
+        "Threefish-512/CFB8/NoPadding",
+        "15f695964f20b95ed72afad75f905788839c53bed2ae5fdfdfb13e3241fd7f94",
+        "Threefish-1024/CFB8/NoPadding",
+        "03d897c89e740d2254f717b73315151d9a34c829e4162232b3cd5f5158ff367b",
         "RC2/CFB8/NoPadding",
         "0aa227f94be3a32ff927c5d25647ea41d7c2a1e94012fc7f2ad6767b9664bce5",
         "RC5/CFB8/NoPadding",
@@ -265,7 +290,29 @@
         "Twofish/ECB/TBCPadding",
         "70336d9c9718a8a2ced1b19deed973a3c58af7ea71a69e7efc4df082dca581c019d7daa58d02b89aab6e8c0d17202439",
         "RC2/ECB/TBCPadding",
-        "eb5b889bbcced12eb6b1a3da6a3d965bba66a5edfdd4c8a6b6b1a3da6a3d965b6b5359ba5e69b179"
+        "eb5b889bbcced12eb6b1a3da6a3d965bba66a5edfdd4c8a6b6b1a3da6a3d965b6b5359ba5e69b179",
+        "DES/CTR/NoPadding",
+        "537572e480c1714fb47081d35eb18eaca9e0a5aee982f105438a0db6cece1f6d",
+        "DESede/CTR/NoPadding",
+        "481e9872acea7fcfa93b7d4e34ec7bab340c10faba2e43b879d40d38e07c422d",
+        "SKIPJACK/CTR/NoPadding",
+        "71143a124e3a0cdeee98a7b843baa05bd1d59faee8ec9b89880e070314a04cc2",
+        "Blowfish/CTR/NoPadding",
+        "6cd6f7c5d2c65555d2b31f8614f54ec654f5e7888d515008d59302c3edfcc6cb",
+        "Twofish/CTR/NoPadding",
+        "821c54b1b54ae113cf74595eefe10c83d09e95d4599190b9bbd5bc71dd703730",
+        "Threefish-256/CTR/NoPadding",
+        "546ea995dd302f1efcb1f27d14bad468280a3a7994c2af75dfdf1e9fc5ef2373",
+        "Threefish-512/CTR/NoPadding",
+        "152df966484ecc2e9ddfc386559732f7f632e4008920804a1bde4efcf2e6e2f2",
+        "Threefish-1024/CTR/NoPadding",
+        "03953ac751a7377812c6e3e4d14b36c6953f9b390acaa892811c10001c9be454",
+        "RC2/CTR/NoPadding",
+        "0a07cb78537cb04c8c5a0a39a15977a7eb19f3c48a42759c234868c391a99c63",
+        "RC5/CTR/NoPadding",
+        "c62b233df296283b97f17364d5f69a1ff91f46659cf9856caefd322a936203a7",
+        "IDEA/CTR/NoPadding",
+        "dd447da3cbdcf81f4694ab7715d79e3f90af5682e8c318b8f7dadbed6b5c9714",
     };
 
     static String[] cipherTests2 =
@@ -280,8 +327,127 @@
         "60fa2f8fae5aa2a38e9ac77d0246726b32df660db51a710ceb7511e451"
     };
 
+    static String[] cipherTestsLargeBlock =
+    {
+        "Threefish-256",
+        "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+             "31533aa864e6a40edc3e24b36260d94374893dc2e479793292e29c18a6ee01a9",
+        "Threefish-512",
+        "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+            "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+            "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+            "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+            "ad7ec86b2137af1ddb64794d714c4e1d7b687b19fc9781ef887a0ad7f88e18fc" +
+            "1baa6123ec8bc497e7eb7b5090cfd756fd5333425ed5a240cb96735dea9713d9",
+        "Threefish-1024",
+        "df6d789e301c6a5e22e0cff0b44666630d44ce774a41b628ebaff6adc86d9e66" +
+            "af50a282a4313552bc9b861cb286ab569e2e23b1c97cdb5cb1fde1bacfba9bfb" +
+            "de3b443218e16b6038537b3d803ff5dbd26b13c177a5bfb597ffccca142a5905" +
+            "8c0f74623daa96bff95b716674701034e7947ce0541426fa5177bc1a519b23ba" +
+            "462f1724989612e49ca5e92a0129ec7be576846fe2616664674e16a29ce8679c" +
+            "0adda9034fbd652910c2ae5afacde10281ab18dbeeb83464dc21ff66b0d358ff" +
+            "2328c73aca59e9095a7bca94acc79d10038eab6ef865545bcf73f4caeeba1844" +
+            "6add98350c8276e5abfb8709bb6c01ef3297b862818a4996b744f375b9126e5c",
+        "Threefish-256/CBC/NoPadding",
+        "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+            "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+            "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3" +
+            "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e",
+        "Threefish-512/CBC/NoPadding",
+        "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+            "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6" +
+            "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+            "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7",
+        "Threefish-1024/CBC/NoPadding",
+        "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87" +
+            "b59be63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb" +
+            "491f280f4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a" +
+            "419544ef5907f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003",
+        "Threefish-256/CBC/PKCS7Padding",
+        "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+            "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+            "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3" +
+            "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" +
+            "f96cb468a5cd39a003f976464a7d072c94cb72a3fe739f101aa7b5452bc3fbba",
+        "Threefish-512/CBC/PKCS7Padding",
+        "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+            "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6" +
+            "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+            "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" +
+            "03902162280012e59efa15c6beecfbf440a6a0c4474bbbb2f74a0ad31bcd398f" +
+            "b24728c3605a4ced3c92c30a5e231113abafaf6f83a3867978e3cdd74091d09f",
+        "Threefish-1024/CBC/PKCS7Padding",
+        "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87" +
+            "b59be63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb" +
+            "491f280f4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a" +
+            "419544ef5907f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003" +
+            "4ddd16ad731ad9a32d0f196a72284f7a8df98918e3e22f1708662edeb1810d2b" +
+            "bafd4200e849f3288b55634b37f99f0f7b2dd192a5944fc211ef9e37b67a829b" +
+            "005a5ec609f736875fdf8946bd79c1daa6c44c9d6733a2223cf8b7e5203b1cfd" +
+            "76995f67e570d9c403b2a2e3f3a89c63c7850ee8d47d4398ac377345a139dda4",
+        "Threefish-256/CTS/NoPadding",
+        "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+            "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+            "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" +
+            "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3",
+        "Threefish-512/CTS/NoPadding",
+        "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+            "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" +
+            "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+            "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6",
+        "Threefish-1024/CTS/NoPadding",
+        "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87b59b" +
+        "e63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb491f280f" +
+        "4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a419544ef5907" +
+        "f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003",
+        "Threefish-256/CBC/WithCTS",
+        "1c46830ef0a43a0869bf070a87f0d4e63f2458edfa5654bafd8520358dae8bf9" +
+            "2a8c039d41e87bb65a907331dde317450d38aba6cb3885bfbe0aee148503e37b" +
+            "0931ce1b791b641dd3e79f2b536897f3c537e3b4588dc03c3888f9bab3bc7a0e" +
+            "973c5e8a16c4309f7a4229d9943ab403082b5836431b9d1646b619f368e057b3",
+        "Threefish-512/CBC/WithCTS",
+        "03efe066d8d6d57b15b04729632de0ce5636b8ccd28219ac17ef836734556e15" +
+            "e90356111279412a814b660150323a416138b2b62942f2d0cd08ee0bb45b0dd7" +
+            "caee9b663eba4663de1cd6f17ffc51dc8b808c95f91e12a818ab31436985830b" +
+            "3aa886a93e53849d34e713f36db52bac3557b137328434f41f825f3948a611c6",
+        "Threefish-1024/CBC/WithCTS",
+        "7540a8fe54a1a1d117ba1f970a12002cf9e24477daef9439dfc43b79a88a9e87b59b" +
+        "e63aa448b4e02e8b9a6464419c35b0b3f97219e6c88ed5429d0f9ffb40bb491f280f" +
+        "4281af177e254828f82e90d196c6bf9afa31926cf5bf0cc3dc81f28a419544ef5907" +
+        "f3b8bf6179da37ff07134d9c6d147521e5c840d5086ec74c1003",
+        "Threefish-256/ECB/TBCPadding",
+        "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "9f82b577cf4cca7a504e9f7a2cd7dbb4ef4ac167c716fca19ab1211f195f610f" +
+            "89c4e79b90153a821bdd4efd5eb1e2cda89b6a91540a003eef03868472d8cfce",
+        "Threefish-512/ECB/TBCPadding",
+        "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+            "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+            "35d0c46770ebb3bf62fadd48765db209df215d7cd18a8b18d11625e70067e1fa" +
+            "bb98982312ce1fdfccae1a59408e1d5418b400a7bf0d1c4e9ea4afa4395886d7" +
+            "dd6bfa1006e4df51298e382ca397a2c398cdb4d65009dce77c5f0a31f9807218" +
+            "a72372a8a0df3b1bacd5dbfb116ebbe314e0b0cd64fd2c8ae8a81491c2534a2a",
+        "Threefish-1024/ECB/TBCPadding",
+        "df6d789e301c6a5e22e0cff0b44666630d44ce774a41b628ebaff6adc86d9e66" +
+            "af50a282a4313552bc9b861cb286ab569e2e23b1c97cdb5cb1fde1bacfba9bfb" +
+            "de3b443218e16b6038537b3d803ff5dbd26b13c177a5bfb597ffccca142a5905" +
+            "8c0f74623daa96bff95b716674701034e7947ce0541426fa5177bc1a519b23ba" +
+            "7312262dc3a25984847d1b05cb624f5751946f136ee7bd0a9a4bbac5dd3bd213" +
+            "702390d3a53d1a4132f59383cce4fe61e08cd3c73c570190d1c8b60940031ef7" +
+            "42f6775b00fb0b4273a14b46a3fc0e760e02f75dc6100ca9c038c3f151e03145" +
+            "92686fd8cccbee74d246a8c59ad80205c9f9aaeb100ea5812837ee8699753301",
+    };
+
     static byte[]   input1 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f");
     static byte[]   input2 = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c");
+    static byte[]   inputLargeBlock = Hex.decode("000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" +
+                                                 "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" +
+                                                 "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f" +
+                                                 "000102030405060708090a0b0c0d0e0fff0102030405060708090a0b0c0d0e0f");
 
     static RC2ParameterSpec rc2Spec = new RC2ParameterSpec(128, Hex.decode("0123456789abcdef"));
     static RC5ParameterSpec rc5Spec = new RC5ParameterSpec(16, 16, 32, Hex.decode("0123456789abcdef"));
@@ -930,6 +1096,11 @@
             test(cipherTests2[i], input2, Hex.decode(cipherTests2[i + 1]));
         }
 
+        for (int i = 0; i != cipherTestsLargeBlock.length; i += 2)
+        {
+            test(cipherTestsLargeBlock[i], inputLargeBlock, Hex.decode(cipherTestsLargeBlock[i + 1]));
+        }
+
         //
         // check for less than a block
         //
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java
index 83063ef..0110d22 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest.java
@@ -1,8 +1,12 @@
 package org.bouncycastle.jce.provider.test;
 
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.bouncycastle.util.encoders.Hex;
-import org.bouncycastle.util.test.SimpleTest;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.Security;
 
 import javax.crypto.Cipher;
 import javax.crypto.CipherInputStream;
@@ -12,13 +16,10 @@
 import javax.crypto.ShortBufferException;
 import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.security.InvalidKeyException;
-import java.security.Key;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.Security;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
 
 /**
  * check that cipher input/output streams are working correctly
@@ -44,6 +45,23 @@
         + "2B4F97E0FF16924A52DF269515110A07"
         + "F9E460BC65EF95DA58F740B7D1DBB0AA");
 
+    private static final byte[] XSK = Hex.decode("d5c7f6797b7e7e9c1d7fd2610b2abf2bc5a7885fb3ff78092fb3abe8986d35e2");
+    private static final byte[] XSIV = Hex.decode("744e17312b27969d826444640e9c4a378ae334f185369c95");
+    private static final byte[] XSIN = Hex.decode("7758298c628eb3a4b6963c5445ef66971222be5d1a4ad839715d1188071739b77cc6e05d5410f963a64167629757");
+    private static final byte[] XSOUT= Hex.decode("27b8cfe81416a76301fd1eec6a4d99675069b2da2776c360db1bdfea7c0aa613913e10f7a60fec04d11e65f2d64e");
+
+    private static final byte[] CHAK = Hex.decode("80000000000000000000000000000000");
+    private static final byte[] CHAIV = Hex.decode("0000000000000000");
+    private static final byte[] CHAIN =  Hex.decode(
+                                              "00000000000000000000000000000000"
+                                            + "00000000000000000000000000000000"
+                                            + "00000000000000000000000000000000"
+                                            + "00000000000000000000000000000000");
+    private static final byte[] CHAOUT = Hex.decode("FBB87FBB8395E05DAA3B1D683C422046"
+                                                    + "F913985C2AD9B23CFC06C1D8D04FF213"
+                                                    + "D44A7A7CDB84929F915420A8A3DC58BF"
+                                                    + "0F7ECB4B1F167BB1A5E6153FDAF4493D");
+
     private static final byte[] HCIN = new byte[64];
     private static final byte[] HCIV = new byte[32];
 
@@ -151,7 +169,7 @@
         byte[] enc = in.doFinal(plainText);
         if (!areEqual(enc, cipherText))
         {
-            fail(name + ": cipher text doesn't match");
+            fail(name + ": cipher text doesn't match got " + new String(Hex.encode(enc)));
         }
 
         byte[] dec = out.doFinal(enc);
@@ -184,7 +202,7 @@
                     (byte)137, (byte)138, (byte)140, (byte)143 };
 
             byte[] keyBytes;
-            if (name.equals("HC256"))
+            if (name.equals("HC256") || name.equals("XSalsa20"))
             {
                 keyBytes = key256;
             }
@@ -207,18 +225,18 @@
                 // too
                 // small to hold the result
                 ecipher.update(new byte[20], 0, 20, cipherText);
-                
+
                 fail("failed exception test - no ShortBufferException thrown");
             }
             catch (ShortBufferException e)
             {
                 // ignore
             }
-            
+
             try
             {
                 Cipher c = Cipher.getInstance(name, "BC");
-    
+
                 Key k = new PublicKey()
                 {
 
@@ -236,22 +254,22 @@
                     {
                         return null;
                     }
-                    
+
                 };
-    
+
                 c.init(Cipher.ENCRYPT_MODE, k);
-    
+
                 fail("failed exception test - no InvalidKeyException thrown for public key");
             }
             catch (InvalidKeyException e)
             {
                 // okay
             }
-            
+
             try
             {
                 Cipher c = Cipher.getInstance(name, "BC");
-    
+
                 Key k = new PrivateKey()
                 {
 
@@ -269,11 +287,11 @@
                     {
                         return null;
                     }
-                    
+
                 };
-    
+
                 c.init(Cipher.DECRYPT_MODE, k);
-    
+
                 fail("failed exception test - no InvalidKeyException thrown for private key");
             }
             catch (InvalidKeyException e)
@@ -286,7 +304,7 @@
             fail("unexpected exception.", e);
         }
     }
-    
+
     public void performTest()
         throws Exception
     {
@@ -296,6 +314,12 @@
         runTest("Salsa20");
         testException("Salsa20");
         testAlgorithm("Salsa20", SK, SIV, SIN, SOUT);
+        runTest("XSalsa20");
+        testException("XSalsa20");
+        testAlgorithm("XSalsa20", XSK, XSIV, XSIN, XSOUT);
+        runTest("ChaCha");
+        testException("ChaCha");
+        testAlgorithm("ChaCha", CHAK, CHAIV, CHAIN, CHAOUT);
         runTest("HC128");
         testException("HC128");
         testAlgorithm("HC128", HCK128A, HCIV, HCIN, HC128A);
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest2.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest2.java
new file mode 100644
index 0000000..de9533c
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/CipherStreamTest2.java
@@ -0,0 +1,495 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.IvParameterSpec;
+
+import org.bouncycastle.crypto.io.InvalidCipherTextIOException;
+import org.bouncycastle.jcajce.io.CipherInputStream;
+import org.bouncycastle.jcajce.io.CipherOutputStream;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class CipherStreamTest2
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "CipherStreamTest";
+    }
+
+    private void testModes(String algo, String[] transforms, boolean authenticated)
+        throws Exception
+    {
+        Key key = generateKey(algo);
+        for (int i = 0; i != transforms.length; i++)
+        {
+            String transform = transforms[i];
+
+            testWriteRead(algo + transform, key, authenticated, true, false);
+            testWriteRead(algo + transform, key, authenticated, true, true);
+            testWriteRead(algo + transform, key, authenticated, false, false);
+            testWriteRead(algo + transform, key, authenticated, false, true);
+            testReadWrite(algo + transform, key, authenticated, true, false);
+            testReadWrite(algo + transform, key, authenticated, true, true);
+            testReadWrite(algo + transform, key, authenticated, false, false);
+            testReadWrite(algo + transform, key, authenticated, false, true);
+
+            if (!(transform.indexOf("CTS") > -1))
+            {
+                testWriteReadEmpty(algo + transform, key, authenticated, true, false);
+                testWriteReadEmpty(algo + transform, key, authenticated, true, true);
+                testWriteReadEmpty(algo + transform, key, authenticated, false, false);
+                testWriteReadEmpty(algo + transform, key, authenticated, false, true);
+            }
+
+            if (authenticated)
+            {
+                testTamperedRead(algo + transform, key, true, true);
+                testTamperedRead(algo + transform, key, true, false);
+                testTruncatedRead(algo + transform, key, true, true);
+                testTruncatedRead(algo + transform, key, true, false);
+                testTamperedWrite(algo + transform, key, true, true);
+                testTamperedWrite(algo + transform, key, true, false);
+            }
+        }
+    }
+
+    private InputStream createInputStream(byte[] data, Cipher cipher, boolean useBc)
+    {
+        ByteArrayInputStream bytes = new ByteArrayInputStream(data);
+        // cast required for earlier JDK
+        return useBc ? (InputStream)new CipherInputStream(bytes, cipher) : (InputStream)new javax.crypto.CipherInputStream(bytes, cipher);
+    }
+
+    private OutputStream createOutputStream(ByteArrayOutputStream bytes, Cipher cipher, boolean useBc)
+    {
+        // cast required for earlier JDK
+        return useBc ? (OutputStream)new CipherOutputStream(bytes, cipher) : (OutputStream)new javax.crypto.CipherOutputStream(bytes, cipher);
+    }
+
+    /**
+     * Test tampering of ciphertext followed by read from decrypting CipherInputStream
+     */
+    private void testTamperedRead(String name, Key key, boolean authenticated, boolean useBc)
+        throws Exception
+    {
+        Cipher encrypt = Cipher.getInstance(name, "BC");
+        Cipher decrypt = Cipher.getInstance(name, "BC");
+        encrypt.init(Cipher.ENCRYPT_MODE, key);
+        if (encrypt.getIV() != null)
+        {
+            decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV()));
+        }
+        else
+        {
+            decrypt.init(Cipher.DECRYPT_MODE, key);
+        }
+
+        byte[] ciphertext = encrypt.doFinal(new byte[1000]);
+
+        // Tamper
+        ciphertext[0] += 1;
+
+        InputStream input = createInputStream(ciphertext, decrypt, useBc);
+        try
+        {
+            while (input.read() >= 0)
+            {
+            }
+            fail("Expected invalid ciphertext after tamper and read : " + name, authenticated, useBc);
+        }
+        catch (InvalidCipherTextIOException e)
+        {
+            // Expected
+        }
+        try
+        {
+            input.close();
+        }
+        catch (Exception e)
+        {
+            fail("Unexpected exception : " + name, e, authenticated, useBc);
+        }
+    }
+
+    /**
+     * Test truncation of ciphertext to make tag calculation impossible, followed by read from
+     * decrypting CipherInputStream
+     */
+    private void testTruncatedRead(String name, Key key, boolean authenticated, boolean useBc)
+        throws Exception
+    {
+        Cipher encrypt = Cipher.getInstance(name, "BC");
+        Cipher decrypt = Cipher.getInstance(name, "BC");
+        encrypt.init(Cipher.ENCRYPT_MODE, key);
+        if (encrypt.getIV() != null)
+        {
+            decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV()));
+        }
+        else
+        {
+            decrypt.init(Cipher.DECRYPT_MODE, key);
+        }
+
+        byte[] ciphertext = encrypt.doFinal(new byte[1000]);
+
+        // Truncate to just smaller than complete tag
+        byte[] truncated = new byte[ciphertext.length - 1000 - 1];
+        System.arraycopy(ciphertext, 0, truncated, 0, truncated.length);
+
+        // Tamper
+        ciphertext[0] += 1;
+
+        InputStream input = createInputStream(truncated, decrypt, useBc);
+        while (true)
+        {
+            int read = 0;
+            try
+            {
+                read = input.read();
+            }
+            catch (InvalidCipherTextIOException e)
+            {
+                // Expected
+                break;
+            }
+            catch (Exception e)
+            {
+                fail("Unexpected exception : " + name, e, authenticated, useBc);
+                break;
+            }
+            if (read < 0)
+            {
+                fail("Expected invalid ciphertext after truncate and read : " + name, authenticated, useBc);
+                break;
+            }
+        }
+        try
+        {
+            input.close();
+        }
+        catch (Exception e)
+        {
+            fail("Unexpected exception : " + name, e, authenticated, useBc);
+        }
+    }
+
+    /**
+     * Test tampering of ciphertext followed by write to decrypting CipherOutputStream
+     */
+    private void testTamperedWrite(String name, Key key, boolean authenticated, boolean useBc)
+        throws Exception
+    {
+        Cipher encrypt = Cipher.getInstance(name, "BC");
+        Cipher decrypt = Cipher.getInstance(name, "BC");
+        encrypt.init(Cipher.ENCRYPT_MODE, key);
+        if (encrypt.getIV() != null)
+        {
+            decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV()));
+        }
+        else
+        {
+            decrypt.init(Cipher.DECRYPT_MODE, key);
+        }
+
+        byte[] ciphertext = encrypt.doFinal(new byte[1000]);
+
+        // Tamper
+        ciphertext[0] += 1;
+
+        ByteArrayOutputStream plaintext = new ByteArrayOutputStream();
+        OutputStream output = createOutputStream(plaintext, decrypt, useBc);
+
+        for (int i = 0; i < ciphertext.length; i++)
+        {
+            output.write(ciphertext[i]);
+        }
+        try
+        {
+            output.close();
+            fail("Expected invalid ciphertext after tamper and write : " + name, authenticated, useBc);
+        }
+        catch (InvalidCipherTextIOException e)
+        {
+            // Expected
+        }
+    }
+
+    /**
+     * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
+     */
+    private void testWriteRead(String name, Key key, boolean authenticated, boolean useBc, boolean blocks)
+        throws Exception
+    {
+        byte[] data = new byte[1000];
+        for (int i = 0; i < data.length; i++)
+        {
+            data[i] = (byte)(i % 255);
+        }
+
+        testWriteRead(name, key, authenticated, useBc, blocks, data);
+    }
+
+    /**
+     * Test CipherOutputStream in ENCRYPT_MODE, CipherInputStream in DECRYPT_MODE
+     */
+    private void testWriteReadEmpty(String name, Key key, boolean authenticated, boolean useBc, boolean blocks)
+        throws Exception
+    {
+        byte[] data = new byte[0];
+
+        testWriteRead(name, key, authenticated, useBc, blocks, data);
+    }
+
+    private void testWriteRead(String name, Key key, boolean authenticated, boolean useBc, boolean blocks, byte[] data)
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        try
+        {
+            Cipher encrypt = Cipher.getInstance(name, "BC");
+            Cipher decrypt = Cipher.getInstance(name, "BC");
+            encrypt.init(Cipher.ENCRYPT_MODE, key);
+            if (encrypt.getIV() != null)
+            {
+                decrypt.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(encrypt.getIV()));
+            }
+            else
+            {
+                decrypt.init(Cipher.DECRYPT_MODE, key);
+            }
+
+            OutputStream cOut = createOutputStream(bOut, encrypt, useBc);
+            if (blocks)
+            {
+                int chunkSize = data.length / 8;
+                for (int i = 0; i < data.length; i += chunkSize)
+                {
+                    cOut.write(data, i, chunkSize);
+                }
+            }
+            else
+            {
+                for (int i = 0; i < data.length; i++)
+                {
+                    cOut.write(data[i]);
+                }
+            }
+            cOut.close();
+
+            byte[] cipherText = bOut.toByteArray();
+            bOut.reset();
+            InputStream cIn = createInputStream(cipherText, decrypt, useBc);
+
+            if (blocks)
+            {
+                byte[] block = new byte[encrypt.getBlockSize() + 1];
+                int c;
+                while ((c = cIn.read(block)) >= 0)
+                {
+                    bOut.write(block, 0, c);
+                }
+            }
+            else
+            {
+                int c;
+                while ((c = cIn.read()) >= 0)
+                {
+                    bOut.write(c);
+                }
+
+            }
+            cIn.close();
+
+        }
+        catch (Exception e)
+        {
+            fail("Unexpected exception " + name, e, authenticated, useBc);
+        }
+
+        byte[] decrypted = bOut.toByteArray();
+        if (!Arrays.areEqual(data, decrypted))
+        {
+            fail("Failed - decrypted data doesn't match: " + name, authenticated, useBc);
+        }
+    }
+
+    protected void fail(String message, boolean authenticated, boolean bc)
+    {
+        if (bc || !authenticated)
+        {
+            super.fail(message);
+        }
+        else
+        {
+            // javax.crypto.CipherInputStream/CipherOutputStream
+            // are broken wrt handling AEAD failures
+            // System.err.println("Broken JCE Streams: " + message);
+        }
+    }
+
+    protected void fail(String message, Throwable throwable, boolean authenticated, boolean bc)
+    {
+        if (bc || !authenticated)
+        {
+            super.fail(message, throwable);
+        }
+        else
+        {
+            // javax.crypto.CipherInputStream/CipherOutputStream
+            // are broken wrt handling AEAD failures
+            //System.err.println("Broken JCE Streams: " + message + " : " + throwable);
+            throwable.printStackTrace();
+        }
+    }
+
+    /**
+     * Test CipherInputStream in ENCRYPT_MODE, CipherOutputStream in DECRYPT_MODE
+     */
+    private void testReadWrite(String name, Key key, boolean authenticated, boolean useBc, boolean blocks)
+        throws Exception
+    {
+        String lCode = "ABCDEFGHIJKLMNOPQRSTU";
+
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        try
+        {
+            Cipher in = Cipher.getInstance(name, "BC");
+            Cipher out = Cipher.getInstance(name, "BC");
+            in.init(Cipher.ENCRYPT_MODE, key);
+            if (in.getIV() != null)
+            {
+                out.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(in.getIV()));
+            }
+            else
+            {
+                out.init(Cipher.DECRYPT_MODE, key);
+            }
+
+            InputStream cIn = createInputStream(lCode.getBytes(), in, useBc);
+            OutputStream cOut = createOutputStream(bOut, out, useBc);
+
+            if (blocks)
+            {
+                byte[] block = new byte[in.getBlockSize() + 1];
+                int c;
+                while ((c = cIn.read(block)) >= 0)
+                {
+                    cOut.write(block, 0, c);
+                }
+            }
+            else
+            {
+                int c;
+                while ((c = cIn.read()) >= 0)
+                {
+                    cOut.write(c);
+                }
+            }
+
+            cIn.close();
+
+            cOut.flush();
+            cOut.close();
+
+        }
+        catch (Exception e)
+        {
+            fail("Unexpected exception " + name, e, authenticated, useBc);
+        }
+
+        String res = new String(bOut.toByteArray());
+        if (!res.equals(lCode))
+        {
+            fail("Failed - decrypted data doesn't match: " + name, authenticated, useBc);
+        }
+    }
+
+    private static Key generateKey(String name)
+        throws Exception
+    {
+        KeyGenerator kGen;
+
+        if (name.indexOf('/') < 0)
+        {
+            kGen = KeyGenerator.getInstance(name, "BC");
+        }
+        else
+        {
+            kGen = KeyGenerator.getInstance(name.substring(0, name.indexOf('/')), "BC");
+        }
+        return kGen.generateKey();
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        final String[] blockCiphers64 = new String[]{"BLOWFISH", "DES", "DESEDE", "TEA", "CAST5", "RC2", "XTEA"};
+
+        for (int i = 0; i != blockCiphers64.length; i++)
+        {
+            testModes(blockCiphers64[i], new String[]{
+                "/ECB/PKCS5Padding",
+                "/CBC/PKCS5Padding",
+                "/OFB/NoPadding",
+                "/CFB/NoPadding",
+                "/CTS/NoPadding",}, false);
+            testModes(blockCiphers64[i], new String[]{"/EAX/NoPadding"}, true);
+        }
+
+        final String[] blockCiphers128 = new String[]{
+            "AES",
+            "NOEKEON",
+            "Twofish",
+            "CAST6",
+            "SEED",
+            "Serpent",
+            "RC6",
+            "CAMELLIA"};
+
+        for (int i = 0; i != blockCiphers128.length; i++)
+        {
+            testModes(blockCiphers128[i], new String[]{
+                "/ECB/PKCS5Padding",
+                "/CBC/PKCS5Padding",
+                "/OFB/NoPadding",
+                "/CFB/NoPadding",
+                "/CTS/NoPadding",
+                "/CTR/NoPadding",
+                "/SIC/NoPadding"}, false);
+            testModes(blockCiphers128[i], new String[]{"/CCM/NoPadding", "/EAX/NoPadding", "/GCM/NoPadding", "/OCB/NoPadding"}, true);
+        }
+
+        final String[] streamCiphers = new String[]{
+            "ARC4",
+            "SALSA20",
+            "XSalsa20",
+            "ChaCha",
+            "Grainv1",
+            "Grain128",
+            "HC128",
+            "HC256"};
+
+        for (int i = 0; i != streamCiphers.length; i++)
+        {
+            testModes(streamCiphers[i], new String[]{""}, false);
+        }
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+        runTest(new CipherStreamTest2());
+    }
+
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java
index c0720be..4ab21ed 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DHTest.java
@@ -28,6 +28,8 @@
 import javax.crypto.spec.DESKeySpec;
 import javax.crypto.spec.DESedeKeySpec;
 import javax.crypto.spec.DHParameterSpec;
+import javax.crypto.spec.DHPrivateKeySpec;
+import javax.crypto.spec.DHPublicKeySpec;
 
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.jcajce.provider.config.ConfigurableProvider;
@@ -264,13 +266,17 @@
     }
 
     private void testTwoParty(String algName, int size, int privateValueSize, KeyPairGenerator keyGen)
+            throws Exception
+    {
+        testTwoParty(algName, size, privateValueSize, keyGen.generateKeyPair(), keyGen.generateKeyPair());
+    }
+
+    private byte[] testTwoParty(String algName, int size, int privateValueSize, KeyPair aKeyPair, KeyPair bKeyPair)
         throws Exception
     {
         //
         // a side
         //
-        KeyPair aKeyPair = keyGen.generateKeyPair();
-
         KeyAgreement aKeyAgree = KeyAgreement.getInstance(algName, "BC");
 
         checkKeySize(privateValueSize, aKeyPair);
@@ -280,8 +286,6 @@
         //
         // b side
         //
-        KeyPair bKeyPair = keyGen.generateKeyPair();
-
         KeyAgreement bKeyAgree = KeyAgreement.getInstance(algName, "BC");
 
         checkKeySize(privateValueSize, bKeyPair);
@@ -294,13 +298,15 @@
         aKeyAgree.doPhase(bKeyPair.getPublic(), true);
         bKeyAgree.doPhase(aKeyPair.getPublic(), true);
 
-        BigInteger  k1 = new BigInteger(aKeyAgree.generateSecret());
-        BigInteger  k2 = new BigInteger(bKeyAgree.generateSecret());
+        byte[] aSecret = aKeyAgree.generateSecret();
+        byte[] bSecret = bKeyAgree.generateSecret();
 
-        if (!k1.equals(k2))
+        if (!Arrays.areEqual(aSecret, bSecret))
         {
             fail(size + " bit 2-way test failed");
         }
+
+        return aSecret;
     }
 
     private void testExplicitWrapping(
@@ -415,7 +421,7 @@
 
         new BouncyCastleProvider().setParameter(ConfigurableProvider.DH_DEFAULT_PARAMS, dhParams);
 
-    KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algName, "BC");
+        KeyPairGenerator keyGen = KeyPairGenerator.getInstance(algName, "BC");
 
         keyGen.initialize(dhParams.getP().bitLength());
 
@@ -727,6 +733,38 @@
         testTwoParty("DH", 512, 0, keyGen);
     }
 
+    private void testSmallSecret()
+        throws Exception
+    {
+        BigInteger p = new BigInteger("ff3b512a4cc0961fa625d6cbd9642c377ece46b8dbc3146a98e0567f944034b5e3a1406edb179a77cd2539bdb74dc819f0a74d486606e26e578ff52c5242a5ff", 16);
+        BigInteger g = new BigInteger("58a66667431136e99d86de8199eb650a21afc9de3dd4ef9da6dfe89c866e928698952d95e68b418becef26f23211572eebfcbf328809bdaf02bba3d24c74f8c0", 16);
+
+        DHPrivateKeySpec aPrivSpec = new DHPrivateKeySpec(
+            new BigInteger("30a6ea4e2240a42867ad98bd3adbfd5b81aba48bd930f20a595983d807566f7cba4e766951efef2c6c0c1be3823f63d66e12c2a091d5ff3bbeb1ea6e335d072d", 16), p, g);
+        DHPublicKeySpec aPubSpec = new DHPublicKeySpec(
+                    new BigInteger("694dfea1bfc8897e2fcbfd88033ab34f4581892d7d5cc362dc056e3d43955accda12222bd651ca31c85f008a05dea914de68828dfd83a54a340fa84f3bbe6caf", 16), p, g);
+
+        DHPrivateKeySpec bPrivSpec = new DHPrivateKeySpec(
+                    new BigInteger("775b1e7e162190700e2212dd8e4aaacf8a2af92c9c108b81d5bf9a14548f494eaa86a6c4844b9512eb3e3f2f22ffec44c795c813edfea13f075b99bbdebb34bd", 16), p, g);
+
+        DHPublicKeySpec bPubSpec = new DHPublicKeySpec(
+                    new BigInteger("d8ddd4ff9246635eadbfa0bc2ef06d98a329b6e8cd2d1435d7b4921467570e697c9a9d3c172c684626a9d2b6b2fa0fc725d5b91f9a9625b717a4169bc714b064", 16), p, g);
+
+        KeyFactory kFact = KeyFactory.getInstance("DH", "BC");
+
+        byte[] secret = testTwoParty("DH", 512, 0, new KeyPair(kFact.generatePublic(aPubSpec), kFact.generatePrivate(aPrivSpec)), new KeyPair(kFact.generatePublic(bPubSpec), kFact.generatePrivate(bPrivSpec)));
+
+        if (secret.length != ((p.bitLength() + 7) / 8))
+        {
+            fail("short secret wrong length");
+        }
+
+        if (!Arrays.areEqual(Hex.decode("00340d3309ddc86e99e2f0be4fc212837bfb5c59336b09b9e1aeb1884b72c8b485b56723d0bf1c1d37fc89a292fc1cface9125106f1df15f55f22e4f77c5879b"), secret))
+        {
+            fail("short secret mismatch");
+        }
+    }
+
     private void testEnc()
         throws Exception
     {
@@ -882,6 +920,7 @@
         testExceptions();
         testDESAndDESede(g768, p768);
         testInitialise();
+        testSmallSecret();
         testConfig();
     }
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DetDSATest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DetDSATest.java
new file mode 100644
index 0000000..16c1969
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DetDSATest.java
@@ -0,0 +1,152 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.math.BigInteger;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.Signature;
+import java.security.spec.DSAPrivateKeySpec;
+import java.security.spec.ECFieldFp;
+import java.security.spec.ECParameterSpec;
+import java.security.spec.ECPoint;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.EllipticCurve;
+
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.nist.NISTNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.math.ec.ECCurve;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * Tests are taken from RFC 6979 - "Deterministic Usage of the Digital Signature Algorithm (DSA) and Elliptic Curve Digital Signature Algorithm (ECDSA)"
+ */
+public class DetDSATest
+    extends SimpleTest
+{
+
+    public static final byte[] SAMPLE = Hex.decode("73616d706c65"); // "sample"
+    public static final byte[] TEST = Hex.decode("74657374"); // "test"
+
+    // test vectors from appendix in RFC 6979
+    private void testHMacDeterministic()
+        throws Exception
+    {
+        DSAPrivateKeySpec privKeySpec = new DSAPrivateKeySpec(new BigInteger("411602CB19A6CCC34494D79D98EF1E7ED5AF25F7", 16),
+            new BigInteger("86F5CA03DCFEB225063FF830A0C769B9DD9D6153AD91D7CE27F787C43278B447" +
+                           "E6533B86B18BED6E8A48B784A14C252C5BE0DBF60B86D6385BD2F12FB763ED88" +
+                           "73ABFD3F5BA2E0A8C0A59082EAC056935E529DAF7C610467899C77ADEDFC846C" +
+                           "881870B7B19B2B58F9BE0521A17002E3BDD6B86685EE90B3D9A1B02B782B1779", 16),
+            new BigInteger("996F967F6C8E388D9E28D01E205FBA957A5698B1", 16),
+            new BigInteger("07B0F92546150B62514BB771E2A0C0CE387F03BDA6C56B505209FF25FD3C133D" +
+                           "89BBCD97E904E09114D9A7DEFDEADFC9078EA544D2E401AEECC40BB9FBBF78FD" +
+                           "87995A10A1C27CB7789B594BA7EFB5C4326A9FE59A070E136DB77175464ADCA4" +
+                           "17BE5DCE2F40D10A46A3A3943F26AB7FD9C0398FF8C76EE0A56826A8A88F1DBD", 16));
+
+        KeyFactory keyFact = KeyFactory.getInstance("DSA", "BC");
+
+        PrivateKey privKey = keyFact.generatePrivate(privKeySpec);
+
+        doTestHMACDetDSASample("SHA1withDETDSA", privKey, new BigInteger("2E1A0C2562B2912CAAF89186FB0F42001585DA55", 16), new BigInteger("29EFB6B0AFF2D7A68EB70CA313022253B9A88DF5", 16));
+        doTestHMACDetDSASample("SHA224withDETDSA", privKey, new BigInteger("4BC3B686AEA70145856814A6F1BB53346F02101E", 16), new BigInteger("410697B92295D994D21EDD2F4ADA85566F6F94C1", 16));
+        doTestHMACDetDSASample("SHA256withDETDSA", privKey, new BigInteger("81F2F5850BE5BC123C43F71A3033E9384611C545", 16), new BigInteger("4CDD914B65EB6C66A8AAAD27299BEE6B035F5E89", 16));
+        doTestHMACDetDSASample("SHA384withDETDSA", privKey, new BigInteger("07F2108557EE0E3921BC1774F1CA9B410B4CE65A", 16), new BigInteger("54DF70456C86FAC10FAB47C1949AB83F2C6F7595", 16));
+        doTestHMACDetDSASample("SHA512withDETDSA", privKey, new BigInteger("16C3491F9B8C3FBBDD5E7A7B667057F0D8EE8E1B", 16), new BigInteger("02C36A127A7B89EDBB72E4FFBC71DABC7D4FC69C", 16));
+    }
+
+    private void doTestHMACDetDSASample(String algName, PrivateKey privKey, BigInteger r, BigInteger s)
+        throws Exception
+    {
+        doTestHMACDetECDSA(Signature.getInstance(algName, "BC"), SAMPLE, privKey, r, s);
+    }
+
+    // test vectors from appendix in RFC 6979
+    private void testECHMacDeterministic()
+        throws Exception
+    {
+        X9ECParameters x9ECParameters = NISTNamedCurves.getByName("P-192");
+        ECCurve curve = x9ECParameters.getCurve();
+
+        ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(new BigInteger("6FAB034934E4C0FC9AE67F5B5659A9D7D1FEFD187EE09FD4", 16),
+            new ECParameterSpec(
+                new EllipticCurve(new ECFieldFp(((ECCurve.Fp)curve).getQ()), curve.getA().toBigInteger(), curve.getB().toBigInteger(), null),
+                new ECPoint(x9ECParameters.getG().getXCoord().toBigInteger(), x9ECParameters.getG().getYCoord().toBigInteger()),
+                x9ECParameters.getN(), x9ECParameters.getH().intValue())
+            );
+
+        KeyFactory keyFact = KeyFactory.getInstance("ECDSA", "BC");
+
+        PrivateKey privKey = keyFact.generatePrivate(privKeySpec);
+
+        doTestHMACDetECDSASample("SHA1withDETECDSA", privKey,   new BigInteger("98C6BD12B23EAF5E2A2045132086BE3EB8EBD62ABF6698FF", 16), new BigInteger("57A22B07DEA9530F8DE9471B1DC6624472E8E2844BC25B64", 16));
+        doTestHMACDetECDSASample("SHA224withDETECDSA", privKey, new BigInteger("A1F00DAD97AEEC91C95585F36200C65F3C01812AA60378F5", 16), new BigInteger("E07EC1304C7C6C9DEBBE980B9692668F81D4DE7922A0F97A", 16));
+        doTestHMACDetECDSASample("SHA256withDETECDSA", privKey, new BigInteger("4B0B8CE98A92866A2820E20AA6B75B56382E0F9BFD5ECB55", 16), new BigInteger("CCDB006926EA9565CBADC840829D8C384E06DE1F1E381B85", 16));
+        doTestHMACDetECDSASample("SHA384withDETECDSA", privKey, new BigInteger("DA63BF0B9ABCF948FBB1E9167F136145F7A20426DCC287D5", 16), new BigInteger("C3AA2C960972BD7A2003A57E1C4C77F0578F8AE95E31EC5E", 16));
+        doTestHMACDetECDSASample("SHA512withDETECDSA", privKey, new BigInteger("4D60C5AB1996BD848343B31C00850205E2EA6922DAC2E4B8", 16), new BigInteger("3F6E837448F027A1BF4B34E796E32A811CBB4050908D8F67", 16));
+
+        doTestHMACDetECDSATest("SHA1withDETECDSA", privKey,   new BigInteger("0F2141A0EBBC44D2E1AF90A50EBCFCE5E197B3B7D4DE036D", 16), new BigInteger("EB18BC9E1F3D7387500CB99CF5F7C157070A8961E38700B7", 16));
+        doTestHMACDetECDSATest("SHA224withDETECDSA", privKey, new BigInteger("6945A1C1D1B2206B8145548F633BB61CEF04891BAF26ED34", 16), new BigInteger("B7FB7FDFC339C0B9BD61A9F5A8EAF9BE58FC5CBA2CB15293", 16));
+        doTestHMACDetECDSATest("SHA256withDETECDSA", privKey, new BigInteger("3A718BD8B4926C3B52EE6BBE67EF79B18CB6EB62B1AD97AE", 16), new BigInteger("5662E6848A4A19B1F1AE2F72ACD4B8BBE50F1EAC65D9124F", 16));
+        doTestHMACDetECDSATest("SHA384withDETECDSA", privKey, new BigInteger("B234B60B4DB75A733E19280A7A6034BD6B1EE88AF5332367", 16), new BigInteger("7994090B2D59BB782BE57E74A44C9A1C700413F8ABEFE77A", 16));
+        doTestHMACDetECDSATest("SHA512withDETECDSA", privKey, new BigInteger("FE4F4AE86A58B6507946715934FE2D8FF9D95B6B098FE739", 16), new BigInteger("74CF5605C98FBA0E1EF34D4B5A1577A7DCF59457CAE52290", 16));
+    }
+
+    private void doTestHMACDetECDSASample(String sigAlg, PrivateKey privKey, BigInteger r, BigInteger s)
+        throws Exception
+    {
+        doTestHMACDetECDSA(Signature.getInstance(sigAlg, "BC"), SAMPLE, privKey, r, s);
+    }
+
+    private void doTestHMACDetECDSATest(String sigAlg, PrivateKey privKey, BigInteger r, BigInteger s)
+        throws Exception
+    {
+        doTestHMACDetECDSA(Signature.getInstance(sigAlg, "BC"), TEST, privKey, r, s);
+    }
+
+    private void doTestHMACDetECDSA(Signature detSigner, byte[] data, PrivateKey privKey, BigInteger r, BigInteger s)
+        throws Exception
+    {
+        detSigner.initSign(privKey);
+
+        detSigner.update(data, 0, data.length);
+
+        byte[] m = detSigner.sign();
+
+        ASN1Sequence seq = ASN1Sequence.getInstance(m);
+
+
+        if (!r.equals(ASN1Integer.getInstance(seq.getObjectAt(0)).getValue()))
+        {
+            fail("r value wrong");
+        }
+        if (!s.equals(ASN1Integer.getInstance(seq.getObjectAt(1)).getValue()))
+        {
+            fail("s value wrong");
+        }
+    }
+
+    public String getName()
+    {
+        return "DetDSA";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        testHMacDeterministic();
+        testECHMacDeterministic();
+    }
+
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new DetDSATest());
+    }
+}
+
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java
index 5e37991..679f3ea 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/DigestTest.java
@@ -30,7 +30,8 @@
         { "RIPEMD320", "de4c01b3054f8930a79d09ae738e92301e5a17085beffdc1b8d116713e74f82fa942d64cdbc4682d" },
         { "Tiger", "2AAB1484E8C158F2BFB8C5FF41B57A525129131C957B5F93" },
         { "GOST3411", "b285056dbf18d7392d7677369524dd14747459ed8143997e163b2986f92fd42c" },
-        { "WHIRLPOOL", "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" }
+        { "WHIRLPOOL", "4E2448A4C6F486BB16B6562C73B4020BF3043E3A731BCE721AE1B303D97E6D4C7181EEBDB6C57E277D0E34957114CBD6C797FC9D95D8B582D225292076D4EEF5" },
+        { "SM3", "66c7f0f462eeedd9d1f2d46bdc10e4e24167c4875cf2f7a2297da02b8f4ba8e0" },
     };
     
     public String getName()
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java
index 0f93bdc..802134c 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECDSA5Test.java
@@ -18,6 +18,7 @@
 import java.security.Signature;
 import java.security.interfaces.ECPrivateKey;
 import java.security.interfaces.ECPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.ECFieldF2m;
 import java.security.spec.ECFieldFp;
 import java.security.spec.ECGenParameterSpec;
@@ -28,6 +29,7 @@
 import java.security.spec.EllipticCurve;
 
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.ASN1Sequence;
 import org.bouncycastle.asn1.DERInteger;
@@ -38,6 +40,7 @@
 import org.bouncycastle.asn1.x9.X962Parameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.jce.ECKeyUtil;
+import org.bouncycastle.jce.ECNamedCurveTable;
 import org.bouncycastle.jce.ECPointUtil;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.BigIntegers;
@@ -687,6 +690,50 @@
         }
     }
 
+    private static class ECRandom
+        extends SecureRandom
+    {
+        public void nextBytes(byte[] bytes)
+        {
+            byte[] src = BigInteger.valueOf(1000).toByteArray();
+            System.arraycopy(src, 0, bytes, bytes.length - src.length, src.length);
+        }
+    }
+
+    private void testNamedCurveParameterPreservation()
+        throws Exception
+    {
+        AlgorithmParameterSpec ecSpec = ECNamedCurveTable.getParameterSpec("secp256r1");
+        KeyPairGenerator keygen = KeyPairGenerator.getInstance("EC", "BC");
+        keygen.initialize(ecSpec, new ECRandom());
+
+        KeyPair keys = keygen.generateKeyPair();
+
+        PrivateKeyInfo priv1 = PrivateKeyInfo.getInstance(keys.getPrivate().getEncoded());
+        SubjectPublicKeyInfo pub1 = SubjectPublicKeyInfo.getInstance(keys.getPublic().getEncoded());
+
+        keygen = KeyPairGenerator.getInstance("EC", "BC");
+        keygen.initialize(new ECGenParameterSpec("secp256r1"), new ECRandom());
+
+        PrivateKeyInfo priv2 = PrivateKeyInfo.getInstance(keys.getPrivate().getEncoded());
+        SubjectPublicKeyInfo pub2 = SubjectPublicKeyInfo.getInstance(keys.getPublic().getEncoded());
+
+        if (!priv1.equals(priv2) || !pub1.equals(pub2))
+        {
+            fail("mismatch between alg param spec and ECGenParameterSpec");
+        }
+
+        if (!(priv2.getPrivateKeyAlgorithm().getParameters() instanceof ASN1ObjectIdentifier))
+        {
+            fail("OID not preserved in private key");
+        }
+
+        if (!(pub1.getAlgorithm().getParameters() instanceof ASN1ObjectIdentifier))
+        {
+            fail("OID not preserved in public key");
+        }
+    }
+
     protected BigInteger[] derDecode(
         byte[]  encoding)
         throws IOException
@@ -718,6 +765,7 @@
         testECDSA239bitBinary();
         testGeneration();
         testKeyPairGenerationWithOIDs();
+        testNamedCurveParameterPreservation();
     }
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java
index 9a5135a..1bd3178 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ECEncodingTest.java
@@ -120,8 +120,8 @@
             {
                 ((ECPointEncoder)pubKey).setPointFormat("UNCOMPRESSED");
             }
-            byte[] x = pubKey.getQ().getX().toBigInteger().toByteArray();
-            byte[] y = pubKey.getQ().getY().toBigInteger().toByteArray();
+            byte[] x = pubKey.getQ().getAffineXCoord().toBigInteger().toByteArray();
+            byte[] y = pubKey.getQ().getAffineYCoord().toBigInteger().toByteArray();
             if (x.length == y.length)
             {
                 success = true;
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java
index b7fecd0..93e3ad7 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/GOST28147Test.java
@@ -11,8 +11,8 @@
 import javax.crypto.CipherOutputStream;
 import javax.crypto.KeyGenerator;
 import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
 import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -182,11 +182,11 @@
     private void oidTest()
     {
         String[] oids = {
-                CryptoProObjectIdentifiers.gostR28147_cbc.getId(),
+                CryptoProObjectIdentifiers.gostR28147_gcfb.getId(),
         };
         
         String[] names = {
-            "GOST28147/CBC/PKCS7Padding"
+            "GOST28147/GCFB/NoPadding"
         };
         
         try
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java
index 103a3e3..fa90108 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/ImplicitlyCaTest.java
@@ -122,12 +122,12 @@
 
         testEncoding(sKey, vKey);
 
-        ECPublicKey vKey2 = (ECPublicKey)fact.generatePublic(new ECPublicKeySpec(vKey.getQ(), ecSpec));
-        ECPrivateKey sKey2 = (ECPrivateKey)fact.generatePrivate(new ECPrivateKeySpec(sKey.getD(), ecSpec));
+        ECPublicKey vKey2 = (ECPublicKey)fact.generatePublic(new ECPublicKeySpec(vKey.getQ(), null));
+        ECPrivateKey sKey2 = (ECPrivateKey)fact.generatePrivate(new ECPrivateKeySpec(sKey.getD(), null));
 
         if (!vKey.equals(vKey2) || vKey.hashCode() != vKey2.hashCode())
         {
-            fail("private equals/hashCode failed");
+            fail("public equals/hashCode failed");
         }
 
         if (!sKey.equals(sKey2) || sKey.hashCode() != sKey2.hashCode())
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OCBTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OCBTest.java
new file mode 100644
index 0000000..c693ce8
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/OCBTest.java
@@ -0,0 +1,104 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class OCBTest
+    extends SimpleTest
+{
+    public String getName()
+    {
+        return "OCB";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        checkRegistrations();
+    }
+
+    private void checkRegistrations()
+        throws Exception
+    {
+        String[] ciphers = new String[] { "AES", "NOEKEON", "Twofish", "CAST6", "SEED", "Serpent", "RC6", "CAMELLIA" };
+        String[] cipherText = new String[]
+            {
+                "BEA5E8798DBE7110031C144DA0B2612213CC8B747807121A4CBB3E4BD6B456AF",
+                "a2545b927e0f2e6db2998e20b17d5fc0564dcab63b748327e2ef4eaed88cb059",
+                "1cfafe72f7181cae331610c116345e51fc356b379aca04da2a53337c5428d8e4",
+                "5b9b738b2ac7000b33b89dd4eec18dd853f4f7c1d9e17b565405f17a0a8c8b63",
+                "fcdbcee69d02c69858ed4569f78b81920b3027cdb7f1f154634aa5ace9e6ba29",
+                "4f7154cb34558940e85db7d3e96ac6c9cb0d9c1b00b18e82e15d1be83deef9df",
+                "23f3e450c4c7199563a0ed601a5c60d75eb88db2a0d090ae5e84d98438a146aa",
+                "ac13ce9db4af148e910a813fc728e5785e23b1bf1d04a961a3f95f356b9417ab"
+            };
+
+        for (int i = 0; i < ciphers.length; i++)
+        {
+            ocbTest(ciphers[i], cipherText[i]);
+        }
+    }
+
+    private void ocbTest(String cipher, String cText)
+        throws Exception
+    {
+        byte[] K = Hex.decode(
+              "000102030405060708090A0B0C0D0E0F");
+        byte[] P = Hex.decode(
+              "000102030405060708090A0B0C0D0E0F");
+        byte[] N = Hex.decode("000102030405060708090A0B");
+        String T = "4CBB3E4BD6B456AF";
+        byte[] C = Hex.decode(cText);
+
+        Key key;
+        Cipher                  in, out;
+
+        key = new SecretKeySpec(K, cipher);
+
+        in = Cipher.getInstance(cipher + "/OCB/NoPadding", "BC");
+        out = Cipher.getInstance(cipher + "/OCB/NoPadding", "BC");
+
+        in.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(N));
+
+        byte[] enc = in.doFinal(P);
+        if (!areEqual(enc, C))
+        {
+            fail("ciphertext doesn't match in OCB got " + new String(Hex.encode(enc)));
+        }
+
+        out.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(N));
+
+        byte[] dec = out.doFinal(C);
+        if (!areEqual(dec, P))
+        {
+            fail("plaintext doesn't match in OCB");
+        }
+
+        try
+        {
+            in = Cipher.getInstance(cipher + "/OCB/PKCS5Padding", "BC");
+
+            fail("bad padding missed in OCB");
+        }
+        catch (NoSuchPaddingException e)
+        {
+            // expected
+        }
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new OCBTest());
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java
index c35c5b8..0828440 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/PKCS12StoreTest.java
@@ -11,6 +11,7 @@
 import java.security.PrivateKey;
 import java.security.PublicKey;
 import java.security.Security;
+import java.security.Signature;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.RSAPrivateKey;
@@ -426,6 +427,49 @@
       + "AHoAeQB0AGsAbwB3AG4AaQBrAGEwMTAhMAkGBSsOAwIaBQAEFKJpUOIj0OtI"
       + "j2CPp38YIFBEqvjsBAi8G+yhJe3A/wICCAA=");
 
+    private byte[] gostPfx = Base64.decode(
+        "MIIHEgIBAzCCBssGCSqGSIb3DQEHAaCCBrwEgga4MIIGtDCCBYEGCSqGSIb3"
+      + "DQEHBqCCBXIwggVuAgEAMIIFZwYJKoZIhvcNAQcBMFUGCSqGSIb3DQEFDTBI"
+      + "MCcGCSqGSIb3DQEFDDAaBAi114+lRrpkXAICCAAwCgYGKoUDAgIKBQAwHQYG"
+      + "KoUDAgIVMBMECLEIQPMsz/ZZBgcqhQMCAh8BgIIFAbu13yJiW/BnSKYKbtv9"
+      + "tDJoTv6l9BVpCCI4tvpzJnMeLBJyVZU4JevcJNii+R1LilVuuB+xc8e7/P4G"
+      + "6TILWmnnispr9KPRAbYRfoCJOa59+TYJMur58wwDuYgMapQAFzsvpzyUWi62"
+      + "o3uQbbLKO9hQCeJW2L+K9cbg8k33MjXMLpnblKpqmZbHTmBJDFR3xGw7IEjD"
+      + "UNqruu7DlHY6jctiVJSii9UNEVetSo9AAzfROxRjROg38VsWxLyO9wEMBv/8"
+      + "H8ur+zOtmQPGqirNXmN+pa08OvZin9kh7CgswW03xIbfsdGGGLRAWtvCnEwJ"
+      + "mS2tEfH1SZcuVLpMomhq3FU/jsc12k+vq/jw4I2cmfDL41ieK72bwNj8xUXu"
+      + "JHeoFSPGX4z+nsJUrFbFG4VBuDs2Y0SCWLyYZvdjvJwYjfqtyi/RoFSZjGHF"
+      + "crstf9YNQ0vW0efCJ7pUBH44OrbnCx5ng2U5jFm1b3HBIKA2RX+Tlhv14MgT"
+      + "KSftPZ67eSmgdsyPuQAdMu6fEdBMpVKMNZNRV565690sqi+1jOmH94TUX8XU"
+      + "2pRQj6eGGLq6lgGnnDabcePUEPXW8zW2KYrDKYJ/1QZmVGldvlqnjZMNhIO+"
+      + "Afsqax/P8RBjMduGqdilGdRzbN8PdhVaN0Ys+WzFxiS9gtaA2yPzcQuedWDN"
+      + "T7sIrfIapgFYmmHRQ7ht4AKj+lmOyNadONYw+ww+8RzHB1d2Kk+iXeZCtvH0"
+      + "XFWJZtuoGKSt/gkI0E2vpDfMbLaczaRC7ityO0iJs25ozP4JhZRBVvOmpxc9"
+      + "YuIetbTnTf1TLJKXDgt1IwPZeugbofSeiNv117lx8VgtvMYFD4W+WQlB8HnO"
+      + "C8NOYjkMPElc6PCMB9gGm0cIu1fKLvY8ycLav93JJjdDuC0kgKLb2+8mC5+2"
+      + "DdMkcfgW6hy4c98xnJs8enCww3A4xkRbMU13zMq70liqmKHV2SSurg5hwUHM"
+      + "ZthT8p988ZBrnqW24lXfMBqTK4YtIBMeMnvKocYBXr96ig3GfahI1Aj2Bw2e"
+      + "bpZTVeayYUd+2xX8JJMdqna6Q61AL8/eUhJUETz5+fgQJtPjcKmdJfVHO6nB"
+      + "vOk1t/rjK17eiXLxHCyvfP+Tw8lSFOhcvr4eIeG8WfsWNRu2eKKosOU7uash"
+      + "QpnvQieqDeijuRxf+tbbJ5D86inwbJqdxra7wNuZXmiaB9gFDzNbNjhtL+6i"
+      + "gUyX/iQHKi9bNK+PH6pdH/gkwnG/juhdgqoNY6GRty/LUOPgXD+r5e/ST16R"
+      + "vnlwrlKp5FzRWBEkem+dhelj3rb+cxKEyvPe3TvIUFcmIlV1VCRQ1fBHtX18"
+      + "eC3a3GprH8c40z3S/kdyk7GlFQ27DRLka+iDN05b+MP5jlgvfqYBKxwLfeNu"
+      + "MpxWoCUvYWiQdMih86/l0H+0o5UB8SqRbpuvr6fY910JCk0hDaO1pgB3HlRz"
+      + "k1vb46pg25heXQm3JmO+ghxjOGliYBWjl8p7AfRS9cjS8ca+X02Mv9Viv7Ce"
+      + "3+Gz0MVwfK98viJ3CFxkaEBlM2LM0IeUQbkHG+YwYaTSfl4GYyrug4F0ZdrA"
+      + "KeY9/kIxa/OJxjcIMs2H+2mSpxmrb7ylmHZ2RB8ITiduRVtO091hn/J7N+eT"
+      + "h6BvLBKIFU+UFUdgjxoDNDk7ao++Mu9T3dQfceFBOYzW9vMQgX30yaPLSdan"
+      + "ZMAP0VtiNjCCASsGCSqGSIb3DQEHAaCCARwEggEYMIIBFDCCARAGCyqGSIb3"
+      + "DQEMCgECoIGyMIGvMFUGCSqGSIb3DQEFDTBIMCcGCSqGSIb3DQEFDDAaBAiQ"
+      + "Owewo16xzQICCAAwCgYGKoUDAgIKBQAwHQYGKoUDAgIVMBMECHSCNJJcQ2VI"
+      + "BgcqhQMCAh8BBFYCyRRpFtZgnsxeK7ZHT+aOyoVmzhtnLrqoBHgV4nJJW2/e"
+      + "UcJjc2Rlbzfd+3L/GWcRGF8Bgn+MjiaAqE64Rzaao9t2hc3myw1WrCfPnoEx"
+      + "VI7OPBM5FzFMMCMGCSqGSIb3DQEJFTEWBBTV7LvI27QWRmHD45X2WKXYs3ct"
+      + "AzAlBgkqhkiG9w0BCRQxGB4WAGMAcABfAGUAeABwAG8AcgB0AGUAZDA+MC4w"
+      + "CgYGKoUDAgIJBQAEIJbGZorQsNM63+xozwEI561cTFVCbyHAEEpkvF3eijT8"
+      + "BAgY5sDtkrVeBQICCAA=");
+
     /**
      * we generate a self signed certificate for the sake of testing - RSA
      */
@@ -482,6 +526,38 @@
         return certGen.generate(privKey);
     }
 
+    private void testGOSTStore()
+        throws Exception
+    {
+        byte[] data = Hex.decode("deadbeef");
+
+        KeyStore pkcs12 = KeyStore.getInstance("PKCS12", "BC");
+
+        pkcs12.load(new ByteArrayInputStream(gostPfx), "1".toCharArray());
+
+        PrivateKey pk = (PrivateKey)pkcs12.getKey("cp_exported", null);
+        Certificate[] pubCerts = pkcs12.getCertificateChain("cp_exported");
+
+        Signature sig = Signature.getInstance("ECGOST3410", "BC");
+
+        sig.initSign(pk);
+
+        sig.update(data);
+
+        byte[] signature = sig.sign();
+
+        sig = Signature.getInstance("ECGOST3410", "BC");
+
+        sig.initVerify(pubCerts[0].getPublicKey());
+
+        sig.update(data);
+
+        if (!sig.verify(signature))
+        {
+            fail("key test failed in GOST store");
+        }
+    }
+
     public void testPKCS12Store()
         throws Exception
     {
@@ -1080,7 +1156,7 @@
         throws Exception
     {
         testPKCS12Store();
-
+        testGOSTStore();
 
         // converter tests
 
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java
new file mode 100644
index 0000000..c147c17
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Poly1305Test.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.crypto.generators.Poly1305KeyGenerator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.TestFailedException;
+
+public class Poly1305Test
+    extends SimpleTest
+{
+    private static final byte[] MASTER_KEY = Hex
+            .decode("95cc0e44d0b79a8856afcae1bec4fe3c01bcb20bfc8b6e03609ddd09f44b060f");
+
+    public String getName()
+    {
+        return "Poly1305";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        checkRegistrations();
+    }
+
+    private void checkRegistrations()
+        throws Exception
+    {
+        List missingMacs = new ArrayList();
+        List missingKeyGens = new ArrayList();
+
+        String[] ciphers = new String[]{"AES", "NOEKEON", "Twofish", "CAST6", "SEED", "Serpent", "RC6", "CAMELLIA"};
+        String[] macs = new String[]{
+                "4bb5e21dd13001ed5faccfcfdaf8a854",
+                "6d601be3d5ebbb9972a64ed3223d913d",
+                "211195296d9afc7b35a1223a79487c87",
+                "f328857a1b653684e73760c804c55b1d",
+                "21cd8adb23ca84eb4dbb12780595bf28",
+                "211195296d9afc7b35a1223a79487c87",
+                "db86de7b1fcae429753d68b1263d7ca0",
+                "11918174f33a2f278fb86554da094112"};
+
+        for (int i = 0; i < ciphers.length; i++)
+        {
+            String cipherName = ciphers[i];
+            Cipher cipher;
+            try
+            {
+                cipher = Cipher.getInstance(cipherName, "BC");
+            } catch (Exception e)
+            {
+                System.err.println(cipherName + ": " + e.getMessage());
+                continue;
+            }
+            int blocksize;
+            try
+            {
+                blocksize = cipher.getBlockSize();
+            } catch (Exception e)
+            {
+                System.err.println(cipherName + ": " + e.getMessage());
+                continue;
+            }
+            // Poly1305 is defined over 128 bit block ciphers
+            if (blocksize == 16)
+            {
+                String macName = "Poly1305-" + cipherName;
+                String macNameAlt = "Poly1305" + cipherName;
+
+                // Check we have a Poly1305 registered for each name
+                checkMac(macName, missingMacs, missingKeyGens, macs[i]);
+                checkMac(macNameAlt, missingMacs, missingKeyGens, macs[i]);
+            }
+        }
+        if (missingMacs.size() != 0)
+        {
+            fail("Did not find Poly1305 registrations for the following ciphers: " + missingMacs);
+        }
+        if (missingKeyGens.size() != 0)
+        {
+            fail("Did not find Poly1305 KeyGenerator registrations for the following macs: " + missingKeyGens);
+        }
+    }
+
+    private void checkMac(String name, List missingMacs, List missingKeyGens, String macOutput)
+    {
+        try
+        {
+            try
+            {
+                KeyGenerator kg = KeyGenerator.getInstance(name);
+                SecretKey key = kg.generateKey();
+
+                try
+                {
+                    Poly1305KeyGenerator.checkKey(key.getEncoded());
+                } catch (IllegalArgumentException e)
+                {
+                    fail("Generated key for algo " + name + " does not match required Poly1305 format.");
+                }
+
+                try
+                {
+                    Mac mac = Mac.getInstance(name);
+                    mac.init(new SecretKeySpec(MASTER_KEY, name), new IvParameterSpec(new byte[16]));
+                    mac.update(new byte[128]);
+                    byte[] bytes = mac.doFinal();
+
+                    if (!Arrays.areEqual(bytes, Hex.decode(macOutput)))
+                    {
+                        fail("wrong mac value computed for " + name, macOutput, new String(Hex.encode(bytes)));
+                    }
+                } catch (NoSuchAlgorithmException e)
+                {
+                    missingMacs.add(name);
+                }
+
+            } catch (NoSuchAlgorithmException e)
+            {
+                missingKeyGens.add(name);
+            }
+        } catch (TestFailedException e)
+        {
+            throw e;
+        } catch (Exception e)
+        {
+            fail("Unexpected error", e);
+        }
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new Poly1305Test());
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java
index a0a0572..c1f4582 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RSATest.java
@@ -43,6 +43,7 @@
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.DigestInfo;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.util.Arrays;
@@ -646,6 +647,29 @@
             fail("public key hashCode check failed");
         }
 
+        //
+        // test an OAEP key
+        //
+        SubjectPublicKeyInfo oaepKey = new SubjectPublicKeyInfo(new AlgorithmIdentifier(PKCSObjectIdentifiers.id_RSAES_OAEP, new RSAESOAEPparams()),
+                                                  SubjectPublicKeyInfo.getInstance(pubKey.getEncoded()).parsePublicKey());
+
+        copyKey = (RSAPublicKey)serializeDeserialize(keyFact.generatePublic(new X509EncodedKeySpec(oaepKey.getEncoded())));
+
+        if (!pubKey.equals(copyKey))
+        {
+            fail("public key equality check failed");
+        }
+
+        if (pubKey.hashCode() != copyKey.hashCode())
+        {
+            fail("public key hashCode check failed");
+        }
+
+        if (!Arrays.areEqual(copyKey.getEncoded(), oaepKey.getEncoded()))
+        {
+            fail("encoding does not match");
+        }
+
         oaepCompatibilityTest("SHA-1", priv2048Key, pub2048Key);
         oaepCompatibilityTest("SHA-224", priv2048Key, pub2048Key);
         oaepCompatibilityTest("SHA-256", priv2048Key, pub2048Key);
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java
index 85972a0..e98330e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/RegressionTest.java
@@ -12,6 +12,7 @@
         new FIPSDESTest(),
         new DESedeTest(),
         new AESTest(),
+        new AEADTest(),
         new CamelliaTest(),
         new SEEDTest(),
         new AESSICTest(),
@@ -44,6 +45,7 @@
         new WrapTest(),
         new DoFinalTest(),
         new CipherStreamTest(),
+        new CipherStreamTest2(),
         new NamedCurveTest(),
         new PKIXTest(),
         new NetscapeCertRequestTest(),
@@ -68,10 +70,15 @@
         new MQVTest(),
         new CMacTest(),
         new GMacTest(),
+        new OCBTest(),
         new DSTU4145Test(),
         new CRL5Test(),
+        new Poly1305Test(),
         new SipHashTest(),
-        new SHA3Test()
+        new SHA3Test(),
+        new SkeinTest(),
+        new Shacal2Test(),
+        new DetDSATest()
     };
 
     public static void main(
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java
index 3db5e98..6d04f97 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SerialisationTest.java
@@ -1,12 +1,5 @@
 package org.bouncycastle.jce.provider.test;
 
-import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
-import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
-import org.bouncycastle.util.encoders.Base64;
-import org.bouncycastle.util.test.SimpleTest;
-
-import javax.crypto.interfaces.DHPrivateKey;
-import javax.crypto.interfaces.DHPublicKey;
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.ObjectInputStream;
@@ -16,6 +9,14 @@
 import java.security.interfaces.RSAPrivateCrtKey;
 import java.security.interfaces.RSAPublicKey;
 
+import javax.crypto.interfaces.DHPrivateKey;
+import javax.crypto.interfaces.DHPublicKey;
+
+import org.bouncycastle.jce.interfaces.ElGamalPrivateKey;
+import org.bouncycastle.jce.interfaces.ElGamalPublicKey;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.test.SimpleTest;
+
 public class SerialisationTest
     extends SimpleTest
 {
@@ -56,6 +57,11 @@
               + "eHNxAH4ABv///////////////v////4AAAABdXEAfgAKAAAAEMDh3xza3MJ4XNak/35BYPt4c3EAfgAG///////////////"
               + "+/////gAAAAF1cQB+AAoAAAADAQABeA==");
 
+    private static byte[] rsaPub2 = Base64.decode(
+                 "rO0ABXNyAD5vcmcuYm91bmN5Y2FzdGxlLmpjYWpjZS5wcm92aWRlci5hc3ltbWV0cmljLnJzYS5CQ1JTQVB1YmxpY0tleS"
+              +  "Uiag5b+myEAgACTAAHbW9kdWx1c3QAFkxqYXZhL21hdGgvQmlnSW50ZWdlcjtMAA5wdWJsaWNFeHBvbmVudHEAfgABeHBz"
+              +  "cgAUamF2YS5tYXRoLkJpZ0ludGVnZXKM/J8fqTv7HQMABkkACGJpdENvdW50SQAJYml0TGVuZ3RoSQATZmlyc3ROb256ZXJvQnl0ZU51bUkADGxvd2VzdFNldEJpdEkABnNpZ251bVsACW1hZ25pdHVkZXQAAltCeHIAEGphdmEubGFuZy5OdW1iZXKGrJUdC5TgiwIAAHhw///////////////+/////gAAAAF1cgACW0Ks8xf4BghU4AIAAHhwAAAAIJqU1y+8k0wz3jgpbPO4fYxeQpuJbXQ4TMZBv0H0wz9PeHNxAH4AA////////////////v////4AAAABdXEAfgAHAAAAAwEAAXg=");
+
     private static BigInteger elGamalY = new BigInteger("89822212135401014750127909969755994242838935150891306006689219384134393835581");
     private static BigInteger elGamalX = new BigInteger("23522982289275336984843296896007818700866293719703239515258104457243931686357");
     private static BigInteger elGamalG = new BigInteger("29672625807664138507782226105202719390719480236799714903174779490259822385963");
@@ -167,6 +173,17 @@
             fail("public key exponent mismatch");
         }
 
+        RSAPublicKey pub2 = (RSAPublicKey)readObject(rsaPub2);
+
+        if (!mod.equals(pub2.getModulus()))
+        {
+            fail("public key 2 modulus mismatch");
+        }
+        if (!pubExp.equals(pub2.getPublicExponent()))
+        {
+            fail("public key 2 exponent mismatch");
+        }
+
         RSAPrivateCrtKey priv = (RSAPrivateCrtKey)readObject(rsaPriv);
 
         if (!mod.equals(priv.getModulus()))
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Shacal2Test.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Shacal2Test.java
new file mode 100644
index 0000000..4b4954a
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/Shacal2Test.java
@@ -0,0 +1,154 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.IOException;
+import java.security.Key;
+import java.security.Security;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.CipherOutputStream;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+/**
+ * basic test class for the Shacal2 cipher, vector from NESSIE (Test vectors set 8, vector# 0)
+ */
+public class Shacal2Test
+    extends SimpleTest
+{
+    static String[] cipherTests =
+        {
+            "512",
+            "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F202122232425262728292A2B2C2D2E2F303132333435363738393A3B3C3D3E3F",
+            "98BCC10405AB0BFC686BECECAAD01AC19B452511BCEB9CB094F905C51CA45430",
+            "00112233445566778899AABBCCDDEEFF102132435465768798A9BACBDCEDFE0F",
+
+        };
+
+    public String getName()
+    {
+        return "Shacal2";
+    }
+
+    public void testECB(
+        int strength,
+        byte[] keyBytes,
+        byte[] input,
+        byte[] output)
+        throws Exception
+    {
+        Key key;
+        Cipher in, out;
+        CipherInputStream cIn;
+        CipherOutputStream cOut;
+        ByteArrayInputStream bIn;
+        ByteArrayOutputStream bOut;
+
+        key = new SecretKeySpec(keyBytes, "Shacal2");
+
+        in = Cipher.getInstance("Shacal2/ECB/NoPadding", "BC");
+        out = Cipher.getInstance("Shacal2/ECB/NoPadding", "BC");
+        try
+        {
+            out.init(Cipher.ENCRYPT_MODE, key);
+        }
+        catch (Exception e)
+        {
+            fail("Shacal2 failed initialisation - " + e.toString(), e);
+        }
+
+        try
+        {
+            in.init(Cipher.DECRYPT_MODE, key);
+        }
+        catch (Exception e)
+        {
+            fail("Shacal2 failed initialisation - " + e.toString(), e);
+        }
+
+        //
+        // encryption pass
+        //
+        bOut = new ByteArrayOutputStream();
+
+        cOut = new CipherOutputStream(bOut, out);
+
+        try
+        {
+            for (int i = 0; i != input.length / 2; i++)
+            {
+                cOut.write(input[i]);
+            }
+            cOut.write(input, input.length / 2, input.length - input.length / 2);
+            cOut.close();
+        }
+        catch (IOException e)
+        {
+            fail("Shacal2 failed encryption - " + e.toString(), e);
+        }
+
+        byte[] bytes;
+
+        bytes = bOut.toByteArray();
+
+        if (!areEqual(bytes, output))
+        {
+            fail("Shacal2 failed encryption - expected " + new String(Hex.encode(output)) + " got " + new String(Hex.encode(bytes)));
+        }
+
+        //
+        // decryption pass
+        //
+        bIn = new ByteArrayInputStream(bytes);
+
+        cIn = new CipherInputStream(bIn, in);
+
+        try
+        {
+            DataInputStream dIn = new DataInputStream(cIn);
+
+            bytes = new byte[input.length];
+
+            for (int i = 0; i != input.length / 2; i++)
+            {
+                bytes[i] = (byte)dIn.read();
+            }
+            dIn.readFully(bytes, input.length / 2, bytes.length - input.length / 2);
+        }
+        catch (Exception e)
+        {
+            fail("Shacal2 failed encryption - " + e.toString(), e);
+        }
+
+        if (!areEqual(bytes, input))
+        {
+            fail("Shacal2 failed decryption - expected " + new String(Hex.encode(input)) + " got " + new String(Hex.encode(bytes)));
+        }
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        for (int i = 0; i != cipherTests.length; i += 4)
+        {
+            testECB(Integer.parseInt(cipherTests[i]),
+                Hex.decode(cipherTests[i + 1]),
+                Hex.decode(cipherTests[i + 2]),
+                Hex.decode(cipherTests[i + 3]));
+        }
+    }
+
+    public static void main(
+        String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new Shacal2Test());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SkeinTest.java b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SkeinTest.java
new file mode 100644
index 0000000..5bc5387
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/jce/provider/test/SkeinTest.java
@@ -0,0 +1,316 @@
+package org.bouncycastle.jce.provider.test;
+
+import java.security.MessageDigest;
+import java.security.Security;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+import org.bouncycastle.jcajce.spec.SkeinParameterSpec;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.util.encoders.Hex;
+import org.bouncycastle.util.test.SimpleTest;
+
+public class SkeinTest
+    extends SimpleTest
+{
+    final static String provider = "BC";
+
+    static private byte[] nullMsg = new byte[0];
+
+    static private String[][] nullVectors =
+    {
+        { "Skein-256-128", "07e8ff2191c5052e1a25914c7c213078" },
+        { "Skein-256-160", "ff800bed6d2044ee9d604a674e3fda50d9b24a72" },
+        { "Skein-256-224", "0fadf1fa39e3837a95b3660b4184d9c2f3cfc94b55d8e7a083278bf8" },
+        { "Skein-256-256", "c8877087da56e072870daa843f176e9453115929094c3a40c463a196c29bf7ba" },
+        { "Skein-512-128", "7c9aff5c3738e3faadc7a5265768def1" },
+        { "Skein-512-160", "49daf1ccebb3544bc93cb5019ba91b0eea8876ee" },
+        { "Skein-512-224", "1541ae9fc3ebe24eb758ccb1fd60c2c31a9ebfe65b220086e7819e25" },
+        { "Skein-512-256", "39ccc4554a8b31853b9de7a1fe638a24cce6b35a55f2431009e18780335d2621" },
+        { "Skein-512-384", "dd5aaf4589dc227bd1eb7bc68771f5baeaa3586ef6c7680167a023ec8ce26980f06c4082c488b4ac9ef313f8cbe70808" },
+        { "Skein-512-512", "bc5b4c50925519c290cc634277ae3d6257212395cba733bbad37a4af0fa06af41fca7903d06564fea7a2d3730dbdb80c1f85562dfcc070334ea4d1d9e72cba7a" },
+        { "Skein-1024-384", "1fdb081963b960e89eaa11b87dda55e8a55a3e1066b30e38d8ae2a45242f7dadfaf06d80ca8a73cd8242ce5eab84c164" },
+        { "Skein-1024-512", "e2943eb0bc0efabd49503a76edf7cfcf072db25bad94ed44fe537284163f3119c47ac6f78699b4272255966e0aba65c75a0a64bd23df6996d1bc3174afd9fa8b" },
+        { "Skein-1024-1024", "0fff9563bb3279289227ac77d319b6fff8d7e9f09da1247b72a0a265cd6d2a62645ad547ed8193db48cff847c06494a03f55666d3b47eb4c20456c9373c86297d630d5578ebd34cb40991578f9f52b18003efa35d3da6553ff35db91b81ab890bec1b189b7f52cb2a783ebb7d823d725b0b4a71f6824e88f68f982eefc6d19c6" },
+    };
+
+    static private byte[] shortMsg = Hex.decode("fbd17c26b61a82e12e125f0d459b96c91ab4837dff22b39b78439430cdfc5dc8"
+            + "78bb393a1a5f79bef30995a85a12923339ba8ab7d8fc6dc5fec6f4ed22c122bb"
+            + "e7eb61981892966de5cef576f71fc7a80d14dab2d0c03940b95b9fb3a727c66a"
+            + "6e1ff0dc311b9aa21a3054484802154c1826c2a27a0914152aeb76f1168d4410");
+
+    static private String[][] shortVectors =
+    {
+        { "Skein-256-128", "9703382ea27dc2913e9d02cd976c582f" },
+        { "Skein-256-160", "0cd491b7715704c3a15a45a1ca8d93f8f646d3a1" },
+        { "Skein-256-224", "afd1e2d0f5b6cd4e1f8b3935fa2497d27ee97e72060adac099543487" },
+        { "Skein-256-256", "4de6fe2bfdaa3717a4261030ef0e044ced9225d066354610842a24a3eafd1dcf" },
+        { "Skein-512-128", "c901b1c04af3da4dce05d7975c419224" },
+        { "Skein-512-160", "ef03079d61b57c6047e15fa2b35b46fa24279539" },
+        { "Skein-512-224", "d9e3219b214e15246a2038f76a573e018ef69b385b3bd0576b558231" },
+        { "Skein-512-256", "809dd3f763a11af90912bbb92bc0d94361cbadab10142992000c88b4ceb88648" },
+        { "Skein-512-384", "825f5cbd5da8807a7b4d3e7bd9cd089ca3a256bcc064cd73a9355bf3ae67f2bf93ac7074b3b19907a0665ba3a878b262" },
+        { "Skein-512-512", "1a0d5abf4432e7c612d658f8dcfa35b0d1ab68b8d6bd4dd115c23cc57b5c5bcdde9bff0ece4208596e499f211bc07594d0cb6f3c12b0e110174b2a9b4b2cb6a9" },
+        { "Skein-1024-384", "9c3d0648c11f31c18395d5e6c8ebd73f43d189843fc45235e2c35e345e12d62bc21a41f65896ddc6a04969654c2e2ce9" },
+        { "Skein-1024-512", "5d0416f49c2d08dfd40a1446169dc6a1d516e23b8b853be4933513051de8d5c26baccffb08d3b16516ba3c6ccf3e9a6c78fff6ef955f2dbc56e1459a7cdba9a5" },
+        { "Skein-1024-1024", "96ca81f586c825d0360aef5acaec49ad55289e1797072eee198b64f349ce65b6e6ed804fe38f05135fe769cc56240ddda5098f620865ce4a4278c77fa2ec6bc31c0f354ca78c7ca81665bfcc5dc54258c3b8310ed421d9157f36c093814d9b25103d83e0ddd89c52d0050e13a64c6140e6388431961685734b1f138fe2243086" },
+    };
+    
+    static private String[][] shortMacVectors = 
+    {
+        { "Skein-Mac-256-128", "738f8b23541d50f691ab60af664c1583" },
+        { "Skein-Mac-256-160", "fe07fe50f99b7683bc16980041d8c045857f1189" },
+        { "Skein-Mac-256-224", "0bc19b185f5bfe50f0dba7ab49cd8ca9440260edd5a392d4bdcd2216" },
+        { "Skein-Mac-256-256", "9837ba53d23afcdabd9fcd614ce9e51c0ebecec7a210df4d3724ed591f026ef1" },
+        { "Skein-Mac-512-128", "6d34f46f2033947da7a9dfb068f4102d" },
+        { "Skein-Mac-512-160", "83cb2effecaa60674c2f9fb2fb6771a9899708ba" },
+        { "Skein-Mac-512-224", "e5f83c032875451f31977cd649c866708cb283a509e99cdfd4d995c5" },
+        { "Skein-Mac-512-256", "ed5507ec551ec944c6ed531990c32907eca885dd3af3d50dd09f1dbef422bb11" },
+        { "Skein-Mac-512-384", "b8f84a212723b92a591d6dc145c1655c70df710e9f3365064abdf79e9288dced2f0f895d81f465c811f1207b43b8cfce" },
+        { "Skein-Mac-512-512", "d13ba582467096a0f862114d97baa218512f39c82c984aa29deee724950d7f0929f726173dd42bc35566b0dbfbf5d2a1552ba6f132de301846714215b64e7f82" },
+        { "Skein-Mac-1024-384", "490dbbd049403e602ee3535181a70ee2eb5ade6d83b519953dd0d93c45729f098b679efcd64b5e3f03cd2fa9f1e70d69" },
+        { "Skein-Mac-1024-512", "ce7f1052fa486309d73058d1d4986f886d966a849c72d196bb2b97fc9fb0b1e69f43a521ebd979f5a5581bd12a0dbd0d1ee27af0929881f1d35c875cc0542ecf" },
+        { "Skein-Mac-1024-1024", "60cd8c755b331bcefe97be5a9fe6f63146d12520ca7b20dbc5c5370dae2ff9815c95fab564329a01eced76f0ecb1944ad52a74e89fa1b6cdcdcee4c71c2c18909c4d1324d279fac5ca2280eea0fa70521cf4ea8c616a3ac6082c2244bec5c1ab3a173faf29d84bec7fb852e278ed57785535c979b33b81465c437cd998c04b95" },
+    };
+
+    static private String[][] shortHMacVectors = 
+        {
+        { "HMAC-Skein-256-128", "926a445d5218605286dfe0542a437012" },
+        { "HMAC-Skein-256-160", "5ebc30295e4562a879f94db531ada465073b8bb7" },
+        { "HMAC-Skein-256-224", "a05b3cfc6b86fda7f5dcf0afbb707dc745fa55279a3f80e2c9977ff1" },
+        { "HMAC-Skein-256-256", "51741f6e8ebf133216ac8e05c7a75a6339351fd2dcc4db04e418521c628a2111" },
+        { "HMAC-Skein-512-128", "ad51f8c7b1b347fe52f0f5c71ae9b8eb" },
+        { "HMAC-Skein-512-160", "e0d06c2d406f32bb14dbb2129176219b62d4f89f" },
+        { "HMAC-Skein-512-224", "e7e5327e2aaa88d0038049e8112db31df223be4c31da24abf03731a8" },
+        { "HMAC-Skein-512-256", "30177414f6e35019cacc2e3ae474b25765e6e0e541e16d754c3dad19df763ab0" },
+        { "HMAC-Skein-512-384", "7f0ba3c1c642cf09eb03d0e3760fe172f22fb263006b1fba5bdea1bfaf6e971c17e039abb0030d1a40ac94a747732cce" },
+        { "HMAC-Skein-512-512", "70d864e7f6cbd446778914a951d1961e646ee17a3da8eae551d29f4fafc540b0457cc9f8064c511b80dc29f8369fb5dc258559542abb5342c4892f22934bf5f1" },
+        { "HMAC-Skein-1024-384", "e7d3465b30b5089e24244e747a91f7cb255596b49843466497c07e120c5c2232f51151b185a1e8a5610f041a85cc59ee" },
+        { "HMAC-Skein-1024-512", "c428059ae2d17ba13e461384c4a64cb0be694909e7a04e4983a4fc16476d644c7764e0019b33ea2a8719f731a579f4f7015da7ec1bc56a4920071ac41da836fe" },
+        { "HMAC-Skein-1024-1024", "3ebd13ec7bf1533c343ac78e1b5146225ce7629787f3997b646139c1b80d6f54cd562b7625419ede8710d76410dfb8617514ca3f7abf17657d2bc96722071adb2a6ecd9795a1ef5e4734b450d588efcbc3220faf53c880e61438bb953e024e48db6a745d2368375ac792be858cd01915e28590d4d6d599be95f6e6ceed7d7d91" },
+        };
+    
+    static private byte[] shortMacMessage = Hex.decode("d3090c72167517f7");
+    static private byte[] shortMacKey = Hex.decode("cb41f1706cde09651203c2d0efbaddf8");
+
+    static private byte[] keyIdentifier = "asecretkey".getBytes();
+    static private byte[] keyIdentifierVector = Hex.decode("ca9970a83997e1c346c4348b54cfc9ba7e19bfba");
+
+    public String getName()
+    {
+        return "Skein";
+    }
+
+    void test(String type, String algorithm, byte[] message, String expected) throws Exception
+    {
+        MessageDigest digest = MessageDigest.getInstance(algorithm, provider);
+
+        byte[] result = digest.digest(message);
+        byte[] result2 = digest.digest(message);
+
+        // test zero results valid
+        if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+        {
+            fail(type + " result not equal for " + algorithm, expected, new String(Hex.encode(result)));
+        }
+
+        // test one digest the same message with the same instance
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail(type + " result object 1 not equal");
+        }
+
+        if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+        {
+            fail(type + " result object 1 not equal");
+        }
+
+        // test two, single byte updates
+        for (int i = 0; i < message.length; i++)
+        {
+            digest.update(message[i]);
+        }
+        result2 = digest.digest();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail(type + " result object 2 not equal");
+        }
+
+        // test three, two half updates
+        digest.update(message, 0, message.length / 2);
+        digest.update(message, message.length / 2, message.length - message.length / 2);
+        result2 = digest.digest();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail(type + " result object 3 not equal");
+        }
+
+        // test four, clone test
+        digest.update(message, 0, message.length / 2);
+        MessageDigest d = (MessageDigest)digest.clone();
+        digest.update(message, message.length / 2, message.length - message.length / 2);
+        result2 = digest.digest();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail(type + " result object 4(a) not equal");
+        }
+
+        d.update(message, message.length / 2, message.length - message.length / 2);
+        result2 = d.digest();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail(type + " result object 4(b) not equal");
+        }
+
+        // test five, check reset() method
+        digest.update(message, 0, message.length / 2);
+        digest.reset();
+        digest.update(message, 0, message.length / 2);
+        digest.update(message, message.length / 2, message.length - message.length / 2);
+        result2 = digest.digest();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail(type + " result object 5 not equal");
+        }
+    }
+
+    private void testMac(String algorithm, byte[] message, byte[] key, String expected) throws Exception
+    {
+        Mac mac = Mac.getInstance(algorithm, provider);
+
+        mac.init(new SecretKeySpec(key, algorithm));
+
+        byte[] result = mac.doFinal(message);
+        byte[] result2 = mac.doFinal(message);
+
+        // test zero results valid
+        if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+        {
+            fail("null result not equal for " + algorithm, expected, new String(Hex.encode(result)));
+        }
+
+        // test one digest the same message with the same instance
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail("Result object 1 not equal");
+        }
+
+        if (!MessageDigest.isEqual(result, Hex.decode(expected)))
+        {
+            fail("Result object 1 not equal");
+        }
+
+        // test two, single byte updates
+        for (int i = 0; i < message.length; i++)
+        {
+            mac.update(message[i]);
+        }
+        result2 = mac.doFinal();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail("Result object 2 not equal");
+        }
+
+        // test three, two half updates
+        mac.update(message, 0, message.length / 2);
+        mac.update(message, message.length / 2, message.length - message.length / 2);
+        result2 = mac.doFinal();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail("Result object 3 not equal");
+        }
+
+        // test five, check reset() method
+        mac.update(message, 0, message.length / 2);
+        mac.reset();
+        mac.update(message, 0, message.length / 2);
+        mac.update(message, message.length / 2, message.length - message.length / 2);
+        result2 = mac.doFinal();
+
+        if (!MessageDigest.isEqual(result, result2))
+        {
+            fail("Result object 5 not equal");
+        }
+
+        // test six, check KeyGenerator
+        KeyGenerator generator = KeyGenerator.getInstance(algorithm, provider);
+
+        mac = Mac.getInstance(algorithm, provider);
+        final SecretKey generatedKey = generator.generateKey();
+        if (generatedKey.getEncoded().length != mac.getMacLength())
+        {
+            fail("Default mac key length for " + algorithm);
+        }
+        mac.init(generatedKey);
+        mac.update(message);
+        mac.doFinal();
+    }
+
+    private void testParameters() throws Exception
+    {
+        Mac mac = Mac.getInstance("Skein-Mac-512-160", provider);
+
+        // test six, init using SkeinParameters
+        mac.init(new SecretKeySpec(shortMacKey, "Skein-Mac-512-160"),
+                new SkeinParameterSpec.Builder().setKeyIdentifier(keyIdentifier).build());
+        byte[] result = mac.doFinal(shortMacMessage);
+
+        if (!MessageDigest.isEqual(result, keyIdentifierVector))
+        {
+            fail("Mac with key identifier failed.", new String(Hex.encode(keyIdentifierVector)),  new String(Hex.encode(result)));
+        }
+    }
+
+    private void testMacKeyGenerators(String algorithm) throws Exception
+    {
+        KeyGenerator gen = KeyGenerator.getInstance(algorithm);
+        
+        int outputSize = Integer.parseInt(algorithm.substring(algorithm.lastIndexOf('-') + 1));
+        SecretKey key = gen.generateKey();
+        
+        if (key.getEncoded().length != (outputSize / 8)) {
+            fail(algorithm + " key length should be equal to output size " + (outputSize) + ", but was " + key.getEncoded().length * 8);
+        }
+    }
+
+    public void performTest() throws Exception
+    {
+        for (int i = 0; i < nullVectors.length; i++)
+        {
+            test("Null message", nullVectors[i][0], nullMsg, nullVectors[i][1]);
+        }
+        for (int i = 0; i < shortVectors.length; i++)
+        {
+            test("Short message", shortVectors[i][0], shortMsg, shortVectors[i][1]);
+        }
+        for (int i = 0; i < shortMacVectors.length; i++)
+        {
+            testMac(shortMacVectors[i][0], shortMacMessage, shortMacKey, shortMacVectors[i][1]);
+            testMacKeyGenerators(shortMacVectors[i][0]);
+        }
+
+        for (int i = 0; i < shortHMacVectors.length; i++)
+        {
+            testMac(shortHMacVectors[i][0], shortMacMessage, shortMacKey, shortHMacVectors[i][1]);
+            testMacKeyGenerators(shortHMacVectors[i][0]);
+        }
+        testParameters();
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        runTest(new SkeinTest());
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
index 84ebf70..b3d239e 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECNamedCurveSpec.java
@@ -49,7 +49,8 @@
     private static ECPoint convertPoint(
         org.bouncycastle.math.ec.ECPoint  g)
     {
-        return new ECPoint(g.getX().toBigInteger(), g.getY().toBigInteger());
+        g = g.normalize();
+        return new ECPoint(g.getAffineXCoord().toBigInteger(), g.getAffineYCoord().toBigInteger());
     }
     
     public ECNamedCurveSpec(
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java
index e774a11..df91412 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECParameterSpec.java
@@ -24,7 +24,7 @@
         BigInteger  n)
     {
         this.curve = curve;
-        this.G = G;
+        this.G = G.normalize();
         this.n = n;
         this.h = BigInteger.valueOf(1);
         this.seed = null;
@@ -37,7 +37,7 @@
         BigInteger  h)
     {
         this.curve = curve;
-        this.G = G;
+        this.G = G.normalize();
         this.n = n;
         this.h = h;
         this.seed = null;
@@ -51,7 +51,7 @@
         byte[]      seed)
     {
         this.curve = curve;
-        this.G = G;
+        this.G = G.normalize();
         this.n = n;
         this.h = h;
         this.seed = seed;
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java
index debab00..0e21a5b 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/ECPublicKeySpec.java
@@ -22,7 +22,14 @@
     {
         super(spec);
 
-        this.q = q;
+        if (q.getCurve() != null)
+        {
+            this.q = q.normalize();
+        }
+        else
+        {
+            this.q = q;
+        }
     }
 
     /**
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java
index 384d871..d03fbfe 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/GOST28147ParameterSpec.java
@@ -1,73 +1,48 @@
-package org.bouncycastle.jce.spec; 
-
-import java.security.spec.AlgorithmParameterSpec;
-
-import org.bouncycastle.crypto.engines.GOST28147Engine;
+package org.bouncycastle.jce.spec;
 
 /**
  * A parameter spec for the GOST-28147 cipher.
+ * @deprecated use  org.bouncycastle.jcajce.spec.GOST28147ParameterSpec
  */
 public class GOST28147ParameterSpec
-    implements AlgorithmParameterSpec
+    extends org.bouncycastle.jcajce.spec.GOST28147ParameterSpec
 {
-    private byte[] iv = null;
-    private byte[] sBox = null;
-
+    /**
+     * @deprecated
+     */
     public GOST28147ParameterSpec(
         byte[] sBox)
     {
-        this.sBox = new byte[sBox.length];
-        
-        System.arraycopy(sBox, 0, this.sBox, 0, sBox.length);
+        super(sBox);
     }
 
+    /**
+     * @deprecated
+     */
     public GOST28147ParameterSpec(
         byte[] sBox,
         byte[] iv)
     {
-        this(sBox);
-        this.iv = new byte[iv.length];
-        
-        System.arraycopy(iv, 0, this.iv, 0, iv.length);
+        super(sBox, iv);
+
     }
-    
+
+    /**
+     * @deprecated
+     */
     public GOST28147ParameterSpec(
         String  sBoxName)
     {
-        this.sBox = GOST28147Engine.getSBox(sBoxName);
+        super(sBoxName);
     }
 
+    /**
+     * @deprecated
+     */
     public GOST28147ParameterSpec(
         String  sBoxName,
         byte[]  iv)
     {
-        this(sBoxName);
-        this.iv = new byte[iv.length];
-        
-        System.arraycopy(iv, 0, this.iv, 0, iv.length);
-    }
-
-    public byte[] getSbox()
-    {
-        return sBox;
-    }
-
-    /**
-     * Returns the IV or null if this parameter set does not contain an IV.
-     *
-     * @return the IV or null if this parameter set does not contain an IV.
-     */
-    public byte[] getIV()
-    {
-        if (iv == null)
-        {
-            return null;
-        }
-
-        byte[]  tmp = new byte[iv.length];
-
-        System.arraycopy(iv, 0, tmp, 0, tmp.length);
-
-        return tmp;
+        super(sBoxName, iv);
     }
 }
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java b/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java
index 2a7ceb5..4111072 100644
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java
+++ b/bcprov/src/main/java/org/bouncycastle/jce/spec/RepeatedSecretKeySpec.java
@@ -1,34 +1,17 @@
 package org.bouncycastle.jce.spec;
 
-
-import javax.crypto.SecretKey;
-
 /**
  * A simple object to indicate that a symmetric cipher should reuse the
  * last key provided.
+ * @deprecated use super class org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec
  */
 public class RepeatedSecretKeySpec
-    implements SecretKey
+    extends org.bouncycastle.jcajce.spec.RepeatedSecretKeySpec
 {
     private String algorithm;
 
     public RepeatedSecretKeySpec(String algorithm)
     {
-        this.algorithm = algorithm;
-    }
-
-    public String getAlgorithm()
-    {
-        return algorithm;
-    }
-
-    public String getFormat()
-    {
-        return null;
-    }
-
-    public byte[] getEncoded()
-    {
-        return null;
+        super(algorithm);
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/jce/spec/package.html b/bcprov/src/main/java/org/bouncycastle/jce/spec/package.html
deleted file mode 100644
index 6f37057..0000000
--- a/bcprov/src/main/java/org/bouncycastle/jce/spec/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Parameter specifications for supporting El Gamal, and Elliptic Curve.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java
new file mode 100644
index 0000000..69ab797
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/AbstractECMultiplier.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public abstract class AbstractECMultiplier implements ECMultiplier
+{
+    public ECPoint multiply(ECPoint p, BigInteger k)
+    {
+        int sign = k.signum();
+        if (sign == 0 || p.isInfinity())
+        {
+            return p.getCurve().getInfinity();
+        }
+
+        ECPoint positive = multiplyPositive(p, k.abs());
+        return sign > 0 ? positive : positive.negate();
+    }
+
+    protected abstract ECPoint multiplyPositive(ECPoint p, BigInteger k);
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/DoubleAddMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/DoubleAddMultiplier.java
new file mode 100644
index 0000000..aae2e00
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/DoubleAddMultiplier.java
@@ -0,0 +1,24 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class DoubleAddMultiplier extends AbstractECMultiplier
+{
+    /**
+     * Joye's double-add algorithm.
+     */
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        ECPoint[] R = new ECPoint[]{ p.getCurve().getInfinity(), p };
+
+        int n = k.bitLength();
+        for (int i = 0; i < n; ++i)
+        {
+            int b = k.testBit(i) ? 1 : 0;
+            int bp = 1 - b;
+            R[bp] = R[bp].twicePlus(R[b]);
+        }
+
+        return R[0];
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
index 78a7a8f..d640b5f 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECAlgorithms.java
@@ -7,16 +7,13 @@
     public static ECPoint sumOfTwoMultiplies(ECPoint P, BigInteger a,
         ECPoint Q, BigInteger b)
     {
-        ECCurve c = P.getCurve();
-        if (!c.equals(Q.getCurve()))
-        {
-            throw new IllegalArgumentException("P and Q must be on same curve");
-        }
+        ECCurve cp = P.getCurve();
+        Q = importPoint(cp, Q);
 
         // Point multiplication for Koblitz curves (using WTNAF) beats Shamir's trick
-        if (c instanceof ECCurve.F2m)
+        if (cp instanceof ECCurve.F2m)
         {
-            ECCurve.F2m f2mCurve = (ECCurve.F2m)c;
+            ECCurve.F2m f2mCurve = (ECCurve.F2m)cp;
             if (f2mCurve.isKoblitz())
             {
                 return P.multiply(a).add(Q.multiply(b));
@@ -48,43 +45,83 @@
     public static ECPoint shamirsTrick(ECPoint P, BigInteger k,
         ECPoint Q, BigInteger l)
     {
-        if (!P.getCurve().equals(Q.getCurve()))
-        {
-            throw new IllegalArgumentException("P and Q must be on same curve");
-        }
+        ECCurve cp = P.getCurve();
+        Q = importPoint(cp, Q);
 
         return implShamirsTrick(P, k, Q, l);
     }
 
-    private static ECPoint implShamirsTrick(ECPoint P, BigInteger k,
+    public static ECPoint importPoint(ECCurve c, ECPoint p)
+    {
+        ECCurve cp = p.getCurve();
+        if (!c.equals(cp))
+        {
+            throw new IllegalArgumentException("Point must be on the same curve");
+        }
+        return c.importPoint(p);
+    }
+
+    static void implMontgomeryTrick(ECFieldElement[] zs, int off, int len)
+    {
+        /*
+         * Uses the "Montgomery Trick" to invert many field elements, with only a single actual
+         * field inversion. See e.g. the paper:
+         * "Fast Multi-scalar Multiplication Methods on Elliptic Curves with Precomputation Strategy Using Montgomery Trick"
+         * by Katsuyuki Okeya, Kouichi Sakurai.
+         */
+
+        ECFieldElement[] c = new ECFieldElement[len];
+        c[0] = zs[off];
+
+        int i = 0;
+        while (++i < len)
+        {
+            c[i] = c[i - 1].multiply(zs[off + i]);
+        }
+
+        ECFieldElement u = c[--i].invert();
+
+        while (i > 0)
+        {
+            int j = off + i--;
+            ECFieldElement tmp = zs[j];
+            zs[j] = c[i].multiply(u);
+            u = u.multiply(tmp);
+        }
+
+        zs[off] = u;
+    }
+
+    static ECPoint implShamirsTrick(ECPoint P, BigInteger k,
         ECPoint Q, BigInteger l)
     {
-        int m = Math.max(k.bitLength(), l.bitLength());
-        ECPoint Z = P.add(Q);
-        ECPoint R = P.getCurve().getInfinity();
+        ECCurve curve = P.getCurve();
+        ECPoint infinity = curve.getInfinity();
 
-        for (int i = m - 1; i >= 0; --i)
+        // TODO conjugate co-Z addition (ZADDC) can return both of these
+        ECPoint PaddQ = P.add(Q);
+        ECPoint PsubQ = P.subtract(Q);
+
+        ECPoint[] points = new ECPoint[]{ Q, PsubQ, P, PaddQ };
+        curve.normalizeAll(points);
+
+        ECPoint[] table = new ECPoint[] {
+            points[3].negate(), points[2].negate(), points[1].negate(),
+            points[0].negate(), infinity, points[0],
+            points[1], points[2], points[3] };
+
+        byte[] jsf = WNafUtil.generateJSF(k, l);
+
+        ECPoint R = infinity;
+
+        int i = jsf.length;
+        while (--i >= 0)
         {
-            R = R.twice();
+            int jsfi = jsf[i];
+            int kDigit = (jsfi >> 4), lDigit = ((jsfi << 28) >> 28);
 
-            if (k.testBit(i))
-            {
-                if (l.testBit(i))
-                {
-                    R = R.add(Z);
-                }
-                else
-                {
-                    R = R.add(P);
-                }
-            }
-            else
-            {
-                if (l.testBit(i))
-                {
-                    R = R.add(Q);
-                }
-            }
+            int index = 4 + (kDigit * 3) + lDigit;
+            R = R.twicePlus(table[index]);
         }
 
         return R;
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
index 58281af..19f0062 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECCurve.java
@@ -3,18 +3,199 @@
 import java.math.BigInteger;
 import java.util.Random;
 
+import org.bouncycastle.util.BigIntegers;
+
 /**
  * base class for an elliptic curve
  */
 public abstract class ECCurve
 {
-    ECFieldElement a, b;
+    public static final int COORD_AFFINE = 0;
+    public static final int COORD_HOMOGENEOUS = 1;
+    public static final int COORD_JACOBIAN = 2;
+    public static final int COORD_JACOBIAN_CHUDNOVSKY = 3;
+    public static final int COORD_JACOBIAN_MODIFIED = 4;
+    public static final int COORD_LAMBDA_AFFINE = 5;
+    public static final int COORD_LAMBDA_PROJECTIVE = 6;
+    public static final int COORD_SKEWED = 7;
+
+    public static int[] getAllCoordinateSystems()
+    {
+        return new int[]{ COORD_AFFINE, COORD_HOMOGENEOUS, COORD_JACOBIAN, COORD_JACOBIAN_CHUDNOVSKY,
+            COORD_JACOBIAN_MODIFIED, COORD_LAMBDA_AFFINE, COORD_LAMBDA_PROJECTIVE, COORD_SKEWED };
+    }
+
+    public class Config
+    {
+        protected int coord;
+        protected ECMultiplier multiplier;
+
+        Config(int coord, ECMultiplier multiplier)
+        {
+            this.coord = coord;
+            this.multiplier = multiplier;
+        }
+
+        public Config setCoordinateSystem(int coord)
+        {
+            this.coord = coord;
+            return this;
+        }
+
+        public Config setMultiplier(ECMultiplier multiplier)
+        {
+            this.multiplier = multiplier;
+            return this;
+        }
+
+        public ECCurve create()
+        {
+            if (!supportsCoordinateSystem(coord))
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
+
+            ECCurve c = cloneCurve();
+            if (c == ECCurve.this)
+            {
+                throw new IllegalStateException("implementation returned current curve");
+            }
+
+            c.coord = coord;
+            c.multiplier = multiplier;
+
+            return c;
+        }
+    }
+
+    protected ECFieldElement a, b;
+    protected int coord = COORD_AFFINE;
+    protected ECMultiplier multiplier = null;
 
     public abstract int getFieldSize();
 
     public abstract ECFieldElement fromBigInteger(BigInteger x);
 
-    public abstract ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression);
+    public Config configure()
+    {
+        return new Config(this.coord, this.multiplier);
+    }
+
+    public ECPoint createPoint(BigInteger x, BigInteger y)
+    {
+        return createPoint(x, y, false);
+    }
+
+    /**
+     * @deprecated per-point compression property will be removed, use {@link #createPoint(BigInteger, BigInteger)}
+     * and refer {@link ECPoint#getEncoded(boolean)}
+     */
+    public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+    {
+        return createRawPoint(fromBigInteger(x), fromBigInteger(y), withCompression);
+    }
+
+    protected abstract ECCurve cloneCurve();
+
+    protected abstract ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression);
+
+    protected ECMultiplier createDefaultMultiplier()
+    {
+        return new WNafL2RMultiplier();
+    }
+
+    public boolean supportsCoordinateSystem(int coord)
+    {
+        return coord == COORD_AFFINE;
+    }
+
+    public PreCompInfo getPreCompInfo(ECPoint p)
+    {
+        checkPoint(p);
+        return p.preCompInfo;
+    }
+
+    /**
+     * Sets the <code>PreCompInfo</code> for a point on this curve. Used by
+     * <code>ECMultiplier</code>s to save the precomputation for this <code>ECPoint</code> for use
+     * by subsequent multiplication.
+     * 
+     * @param point
+     *            The <code>ECPoint</code> to store precomputations for.
+     * @param preCompInfo
+     *            The values precomputed by the <code>ECMultiplier</code>.
+     */
+    public void setPreCompInfo(ECPoint point, PreCompInfo preCompInfo)
+    {
+        checkPoint(point);
+        point.preCompInfo = preCompInfo;
+    }
+
+    public ECPoint importPoint(ECPoint p)
+    {
+        if (this == p.getCurve())
+        {
+            return p;
+        }
+        if (p.isInfinity())
+        {
+            return getInfinity();
+        }
+
+        // TODO Default behaviour could be improved if the two curves have the same coordinate system by copying any Z coordinates.
+        p = p.normalize();
+
+        return createPoint(p.getXCoord().toBigInteger(), p.getYCoord().toBigInteger(), p.withCompression);
+    }
+
+    /**
+     * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+     * coordinates reflect those of the equivalent point in an affine coordinate system. Where more
+     * than one point is to be normalized, this method will generally be more efficient than
+     * normalizing each point separately.
+     * 
+     * @param points
+     *            An array of points that will be updated in place with their normalized versions,
+     *            where necessary
+     */
+    public void normalizeAll(ECPoint[] points)
+    {
+        checkPoints(points);
+
+        if (this.getCoordinateSystem() == ECCurve.COORD_AFFINE)
+        {
+            return;
+        }
+
+        /*
+         * Figure out which of the points actually need to be normalized
+         */
+        ECFieldElement[] zs = new ECFieldElement[points.length];
+        int[] indices = new int[points.length];
+        int count = 0;
+        for (int i = 0; i < points.length; ++i)
+        {
+            ECPoint p = points[i];
+            if (null != p && !p.isNormalized())
+            {
+                zs[count] = p.getZCoord(0);
+                indices[count++] = i;
+            }
+        }
+
+        if (count == 0)
+        {
+            return;
+        }
+
+        ECAlgorithms.implMontgomeryTrick(zs, 0, count);
+
+        for (int j = 0; j < count; ++j)
+        {
+            int index = indices[j];
+            points[index] = points[index].normalize(zs[j]);
+        }
+    }
 
     public abstract ECPoint getInfinity();
 
@@ -28,9 +209,26 @@
         return b;
     }
 
+    public int getCoordinateSystem()
+    {
+        return coord;
+    }
+
     protected abstract ECPoint decompressPoint(int yTilde, BigInteger X1);
 
     /**
+     * Sets the default <code>ECMultiplier</code>, unless already set. 
+     */
+    public ECMultiplier getMultiplier()
+    {
+        if (this.multiplier == null)
+        {
+            this.multiplier = createDefaultMultiplier();
+        }
+        return this.multiplier;
+    }
+
+    /**
      * Decode a point on this curve from its ASN.1 encoding. The different
      * encodings are taken account of, including point compression for
      * <code>F<sub>p</sub></code> (X9.62 s 4.2.1 pg 17).
@@ -62,9 +260,9 @@
             }
 
             int yTilde = encoded[0] & 1;
-            BigInteger X1 = fromArray(encoded, 1, expectedLength);
+            BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength);
 
-            p = decompressPoint(yTilde, X1);
+            p = decompressPoint(yTilde, X);
             break;
         }
         case 0x04: // uncompressed
@@ -76,10 +274,10 @@
                 throw new IllegalArgumentException("Incorrect length for uncompressed/hybrid encoding");
             }
 
-            BigInteger X1 = fromArray(encoded, 1, expectedLength);
-            BigInteger Y1 = fromArray(encoded, 1 + expectedLength, expectedLength);
+            BigInteger X = BigIntegers.fromUnsignedByteArray(encoded, 1, expectedLength);
+            BigInteger Y = BigIntegers.fromUnsignedByteArray(encoded, 1 + expectedLength, expectedLength);
 
-            p = createPoint(X1, Y1, false);
+            p = createPoint(X, Y);
             break;
         }
         default:
@@ -89,11 +287,29 @@
         return p;
     }
 
-    private static BigInteger fromArray(byte[] buf, int off, int length)
+    protected void checkPoint(ECPoint point)
     {
-        byte[] mag = new byte[length];
-        System.arraycopy(buf, off, mag, 0, length);
-        return new BigInteger(1, mag);
+        if (null == point || (this != point.getCurve()))
+        {
+            throw new IllegalArgumentException("'point' must be non-null and on this curve");
+        }
+    }
+
+    protected void checkPoints(ECPoint[] points)
+    {
+        if (points == null)
+        {
+            throw new IllegalArgumentException("'points' cannot be null");
+        }
+
+        for (int i = 0; i < points.length; ++i)
+        {
+            ECPoint point = points[i];
+            if (null != point && this != point.getCurve())
+            {
+                throw new IllegalArgumentException("'points' entries must be null or on this curve");
+            }
+        }
     }
 
     /**
@@ -101,15 +317,50 @@
      */
     public static class Fp extends ECCurve
     {
-        BigInteger q;
+        private static final int FP_DEFAULT_COORDS = COORD_JACOBIAN_MODIFIED;
+
+        BigInteger q, r;
         ECPoint.Fp infinity;
 
         public Fp(BigInteger q, BigInteger a, BigInteger b)
         {
             this.q = q;
+            this.r = ECFieldElement.Fp.calculateResidue(q);
+            this.infinity = new ECPoint.Fp(this, null, null);
+
             this.a = fromBigInteger(a);
             this.b = fromBigInteger(b);
+            this.coord = FP_DEFAULT_COORDS;
+        }
+
+        protected Fp(BigInteger q, BigInteger r, ECFieldElement a, ECFieldElement b)
+        {
+            this.q = q;
+            this.r = r;
             this.infinity = new ECPoint.Fp(this, null, null);
+
+            this.a = a;
+            this.b = b;
+            this.coord = FP_DEFAULT_COORDS;
+        }
+
+        protected ECCurve cloneCurve()
+        {
+            return new Fp(q, r, a, b);
+        }
+
+        public boolean supportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+            case COORD_AFFINE:
+            case COORD_HOMOGENEOUS:
+            case COORD_JACOBIAN:
+            case COORD_JACOBIAN_MODIFIED:
+                return true;
+            default:
+                return false;
+            }
         }
 
         public BigInteger getQ()
@@ -124,12 +375,34 @@
 
         public ECFieldElement fromBigInteger(BigInteger x)
         {
-            return new ECFieldElement.Fp(this.q, x);
+            return new ECFieldElement.Fp(this.q, this.r, x);
         }
 
-        public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
+        protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
         {
-            return new ECPoint.Fp(this, fromBigInteger(x), fromBigInteger(y), withCompression);
+            return new ECPoint.Fp(this, x, y, withCompression);
+        }
+
+        public ECPoint importPoint(ECPoint p)
+        {
+            if (this != p.getCurve() && this.getCoordinateSystem() == COORD_JACOBIAN && !p.isInfinity())
+            {
+                switch (p.getCurve().getCoordinateSystem())
+                {
+                case COORD_JACOBIAN:
+                case COORD_JACOBIAN_CHUDNOVSKY:
+                case COORD_JACOBIAN_MODIFIED:
+                    return new ECPoint.Fp(this,
+                        fromBigInteger(p.x.toBigInteger()),
+                        fromBigInteger(p.y.toBigInteger()),
+                        new ECFieldElement[]{ fromBigInteger(p.zs[0].toBigInteger()) },
+                        p.withCompression);
+                default:
+                    break;
+                }
+            }
+
+            return super.importPoint(p);
         }
 
         protected ECPoint decompressPoint(int yTilde, BigInteger X1)
@@ -148,9 +421,7 @@
             }
 
             BigInteger betaValue = beta.toBigInteger();
-            int bit0 = betaValue.testBit(0) ? 1 : 0;
-
-            if (bit0 != yTilde)
+            if (betaValue.testBit(0) != (yTilde == 1))
             {
                 // Use the other root
                 beta = fromBigInteger(q.subtract(betaValue));
@@ -195,6 +466,8 @@
      */
     public static class F2m extends ECCurve
     {
+        private static final int F2M_DEFAULT_COORDS = COORD_AFFINE;
+
         /**
          * The exponent <code>m</code> of <code>F<sub>2<sup>m</sup></sub></code>.
          */
@@ -401,9 +674,53 @@
                 }
             }
 
+            this.infinity = new ECPoint.F2m(this, null, null);
             this.a = fromBigInteger(a);
             this.b = fromBigInteger(b);
+            this.coord = F2M_DEFAULT_COORDS;
+        }
+
+        protected F2m(int m, int k1, int k2, int k3, ECFieldElement a, ECFieldElement b, BigInteger n, BigInteger h)
+        {
+            this.m = m;
+            this.k1 = k1;
+            this.k2 = k2;
+            this.k3 = k3;
+            this.n = n;
+            this.h = h;
+
             this.infinity = new ECPoint.F2m(this, null, null);
+            this.a = a;
+            this.b = b;
+            this.coord = F2M_DEFAULT_COORDS;
+        }
+
+        protected ECCurve cloneCurve()
+        {
+            return new F2m(m, k1, k2, k3, a, b, n, h);
+        }
+
+        public boolean supportsCoordinateSystem(int coord)
+        {
+            switch (coord)
+            {
+            case COORD_AFFINE:
+            case COORD_HOMOGENEOUS:
+            case COORD_LAMBDA_PROJECTIVE:
+                return true;
+            default:
+                return false;
+            }
+        }
+
+        protected ECMultiplier createDefaultMultiplier()
+        {
+            if (isKoblitz())
+            {
+                return new WTauNafMultiplier();
+            }
+
+            return super.createDefaultMultiplier();
         }
 
         public int getFieldSize()
@@ -418,7 +735,32 @@
 
         public ECPoint createPoint(BigInteger x, BigInteger y, boolean withCompression)
         {
-            return new ECPoint.F2m(this, fromBigInteger(x), fromBigInteger(y), withCompression);
+            ECFieldElement X = fromBigInteger(x), Y = fromBigInteger(y);
+
+            switch (this.getCoordinateSystem())
+            {
+            case COORD_LAMBDA_AFFINE:
+            case COORD_LAMBDA_PROJECTIVE:
+            {
+                if (!X.isZero())
+                {
+                    // Y becomes Lambda (X + Y/X) here
+                    Y = Y.divide(X).add(X);
+                }
+                break;
+            }
+            default:
+            {
+                break;
+            }
+            }
+
+            return createRawPoint(X, Y, withCompression);
+        }
+
+        protected ECPoint createRawPoint(ECFieldElement x, ECFieldElement y, boolean withCompression)
+        {
+            return new ECPoint.F2m(this, x, y, withCompression);
         }
 
         public ECPoint getInfinity()
@@ -432,10 +774,7 @@
          */
         public boolean isKoblitz()
         {
-            return ((n != null) && (h != null) &&
-                    ((a.toBigInteger().equals(ECConstants.ZERO)) ||
-                    (a.toBigInteger().equals(ECConstants.ONE))) &&
-                    (b.toBigInteger().equals(ECConstants.ONE)));
+            return n != null && h != null && a.bitLength() <= 1 && b.bitLength() == 1;
         }
 
         /**
@@ -480,7 +819,7 @@
         {
             ECFieldElement xp = fromBigInteger(X1);
             ECFieldElement yp = null;
-            if (xp.toBigInteger().equals(ECConstants.ZERO))
+            if (xp.isZero())
             {
                 yp = (ECFieldElement.F2m)b;
                 for (int i = 0; i < m - 1; i++)
@@ -491,17 +830,31 @@
             else
             {
                 ECFieldElement beta = xp.add(a).add(b.multiply(xp.square().invert()));
-                ECFieldElement z = solveQuadradicEquation(beta);
+                ECFieldElement z = solveQuadraticEquation(beta);
                 if (z == null)
                 {
                     throw new IllegalArgumentException("Invalid point compression");
                 }
-                int zBit = z.toBigInteger().testBit(0) ? 1 : 0;
-                if (zBit != yTilde)
+                if (z.testBitZero() != (yTilde == 1))
                 {
-                    z = z.add(fromBigInteger(ECConstants.ONE));
+                    z = z.addOne();
                 }
+
                 yp = xp.multiply(z);
+
+                switch (this.getCoordinateSystem())
+                {
+                case COORD_LAMBDA_AFFINE:
+                case COORD_LAMBDA_PROJECTIVE:
+                {
+                    yp = yp.divide(xp).add(xp);
+                    break;
+                }
+                default:
+                {
+                    break;
+                }
+                }
             }
 
             return new ECPoint.F2m(this, xp, yp, true);
@@ -512,28 +865,26 @@
          * D.1.6) The other solution is <code>z + 1</code>.
          * 
          * @param beta
-         *            The value to solve the qradratic equation for.
+         *            The value to solve the quadratic equation for.
          * @return the solution for <code>z<sup>2</sup> + z = beta</code> or
          *         <code>null</code> if no solution exists.
          */
-        private ECFieldElement solveQuadradicEquation(ECFieldElement beta)
+        private ECFieldElement solveQuadraticEquation(ECFieldElement beta)
         {
-            ECFieldElement zeroElement = new ECFieldElement.F2m(
-                    this.m, this.k1, this.k2, this.k3, ECConstants.ZERO);
-
-            if (beta.toBigInteger().equals(ECConstants.ZERO))
+            if (beta.isZero())
             {
-                return zeroElement;
+                return beta;
             }
 
+            ECFieldElement zeroElement = fromBigInteger(ECConstants.ZERO);
+
             ECFieldElement z = null;
-            ECFieldElement gamma = zeroElement;
+            ECFieldElement gamma = null;
 
             Random rand = new Random();
             do
             {
-                ECFieldElement t = new ECFieldElement.F2m(this.m, this.k1,
-                        this.k2, this.k3, new BigInteger(m, rand));
+                ECFieldElement t = fromBigInteger(new BigInteger(m, rand));
                 z = zeroElement;
                 ECFieldElement w = beta;
                 for (int i = 1; i <= m - 1; i++)
@@ -542,13 +893,13 @@
                     z = z.square().add(w2.multiply(t));
                     w = w2.add(beta);
                 }
-                if (!w.toBigInteger().equals(ECConstants.ZERO))
+                if (!w.isZero())
                 {
                     return null;
                 }
                 gamma = z.square().add(z);
             }
-            while (gamma.toBigInteger().equals(ECConstants.ZERO));
+            while (gamma.isZero());
 
             return z;
         }
@@ -567,7 +918,7 @@
             }
 
             ECCurve.F2m other = (ECCurve.F2m)anObject;
-            
+
             return (this.m == other.m) && (this.k1 == other.k1)
                 && (this.k2 == other.k2) && (this.k3 == other.k3)
                 && a.equals(other.a) && b.equals(other.b);
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
index b5e9aa5..87608eb 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECFieldElement.java
@@ -3,14 +3,17 @@
 import java.math.BigInteger;
 import java.util.Random;
 
+import org.bouncycastle.util.Arrays;
+import org.bouncycastle.util.BigIntegers;
+
 public abstract class ECFieldElement
     implements ECConstants
 {
-
     public abstract BigInteger     toBigInteger();
     public abstract String         getFieldName();
     public abstract int            getFieldSize();
     public abstract ECFieldElement add(ECFieldElement b);
+    public abstract ECFieldElement addOne();
     public abstract ECFieldElement subtract(ECFieldElement b);
     public abstract ECFieldElement multiply(ECFieldElement b);
     public abstract ECFieldElement divide(ECFieldElement b);
@@ -19,27 +22,99 @@
     public abstract ECFieldElement invert();
     public abstract ECFieldElement sqrt();
 
+    public int bitLength()
+    {
+        return toBigInteger().bitLength();
+    }
+
+    public boolean isZero()
+    {
+        return 0 == toBigInteger().signum();
+    }
+
+    public boolean testBitZero()
+    {
+        return toBigInteger().testBit(0);
+    }
+
     public String toString()
     {
-        return this.toBigInteger().toString(2);
+        return this.toBigInteger().toString(16);
+    }
+
+    public byte[] getEncoded()
+    {
+        return BigIntegers.asUnsignedByteArray((getFieldSize() + 7) / 8, toBigInteger());
     }
 
     public static class Fp extends ECFieldElement
     {
-        BigInteger x;
+        BigInteger q, r, x;
 
-        BigInteger q;
-        
+//        static int[] calculateNaf(BigInteger p)
+//        {
+//            int[] naf = WNafUtil.generateCompactNaf(p);
+//
+//            int bit = 0;
+//            for (int i = 0; i < naf.length; ++i)
+//            {
+//                int ni = naf[i];
+//                int digit = ni >> 16, zeroes = ni & 0xFFFF;
+//
+//                bit += zeroes;
+//                naf[i] = digit < 0 ? ~bit : bit;
+//                ++bit;
+//            }
+//
+//            int last = naf.length - 1;
+//            if (last > 0 && last <= 16)
+//            {
+//                int top = naf[last], top2 = naf[last - 1];
+//                if (top2 < 0)
+//                {
+//                    top2 = ~top2;
+//                }
+//                if (top - top2 >= 64)
+//                {
+//                    return naf;
+//                }
+//            }
+//
+//            return null;
+//        }
+
+        static BigInteger calculateResidue(BigInteger p)
+        {
+            int bitLength = p.bitLength();
+            if (bitLength > 128)
+            {
+                BigInteger firstWord = p.shiftRight(bitLength - 64);
+                if (firstWord.longValue() == -1L)
+                {
+                    return ONE.shiftLeft(bitLength).subtract(p);
+                }
+            }
+            return null;
+        }
+
+        /**
+         * @deprecated Use ECCurve.fromBigInteger to construct field elements
+         */
         public Fp(BigInteger q, BigInteger x)
         {
-            this.x = x;
-            
-            if (x.compareTo(q) >= 0)
+            this(q, calculateResidue(q), x);
+        }
+
+        Fp(BigInteger q, BigInteger r, BigInteger x)
+        {
+            if (x == null || x.signum() < 0 || x.compareTo(q) >= 0)
             {
-                throw new IllegalArgumentException("x value too large in field element");
+                throw new IllegalArgumentException("x value invalid in Fp field element");
             }
 
             this.q = q;
+            this.r = r;
+            this.x = x;
         }
 
         public BigInteger toBigInteger()
@@ -66,40 +141,70 @@
         {
             return q;
         }
-        
+
         public ECFieldElement add(ECFieldElement b)
         {
-            return new Fp(q, x.add(b.toBigInteger()).mod(q));
+            return new Fp(q, r, modAdd(x, b.toBigInteger()));
+        }
+
+        public ECFieldElement addOne()
+        {
+            BigInteger x2 = x.add(ECConstants.ONE);
+            if (x2.compareTo(q) == 0)
+            {
+                x2 = ECConstants.ZERO;
+            }
+            return new Fp(q, r, x2);
         }
 
         public ECFieldElement subtract(ECFieldElement b)
         {
-            return new Fp(q, x.subtract(b.toBigInteger()).mod(q));
+            BigInteger x2 = b.toBigInteger();
+            BigInteger x3 = x.subtract(x2);
+            if (x3.signum() < 0)
+            {
+                x3 = x3.add(q);
+            }
+            return new Fp(q, r, x3);
         }
 
         public ECFieldElement multiply(ECFieldElement b)
         {
-            return new Fp(q, x.multiply(b.toBigInteger()).mod(q));
+            return new Fp(q, r, modMult(x, b.toBigInteger()));
         }
 
         public ECFieldElement divide(ECFieldElement b)
         {
-            return new Fp(q, x.multiply(b.toBigInteger().modInverse(q)).mod(q));
+            return new Fp(q, modMult(x, b.toBigInteger().modInverse(q)));
         }
 
         public ECFieldElement negate()
         {
-            return new Fp(q, x.negate().mod(q));
+            BigInteger x2;
+            if (x.signum() == 0)
+            {
+                x2 = x;
+            }
+            else if (ONE.equals(r))
+            {
+                x2 = q.xor(x);
+            }
+            else
+            {
+                x2 = q.subtract(x);
+            }
+            return new Fp(q, r, x2);
         }
 
         public ECFieldElement square()
         {
-            return new Fp(q, x.multiply(x).mod(q));
+            return new Fp(q, r, modMult(x, x));
         }
 
         public ECFieldElement invert()
         {
-            return new Fp(q, x.modInverse(q));
+            // TODO Modular inversion can be faster for a (Generalized) Mersenne Prime.
+            return new Fp(q, r, x.modInverse(q));
         }
 
         // D.1.4 91
@@ -120,7 +225,7 @@
             if (q.testBit(1))
             {
                 // z = g^(u+1) + p, p = 4u + 3
-                ECFieldElement z = new Fp(q, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q));
+                ECFieldElement z = new Fp(q, r, x.modPow(q.shiftRight(2).add(ECConstants.ONE), q));
 
                 return z.square().equals(this) ? z : null;
             }
@@ -138,7 +243,7 @@
             BigInteger k = u.shiftLeft(1).add(ECConstants.ONE);
 
             BigInteger Q = this.x;
-            BigInteger fourQ = Q.shiftLeft(2).mod(q);
+            BigInteger fourQ = modDouble(modDouble(Q));
 
             BigInteger U, V;
             Random rand = new Random();
@@ -152,11 +257,11 @@
                 while (P.compareTo(q) >= 0
                     || !(P.multiply(P).subtract(fourQ).modPow(legendreExponent, q).equals(qMinusOne)));
 
-                BigInteger[] result = lucasSequence(q, P, Q, k);
+                BigInteger[] result = lucasSequence(P, Q, k);
                 U = result[0];
                 V = result[1];
 
-                if (V.multiply(V).mod(q).equals(fourQ))
+                if (modMult(V, V).equals(fourQ))
                 {
                     // Integer division by 2, mod q
                     if (V.testBit(0))
@@ -168,7 +273,7 @@
 
                     //assert V.multiply(V).mod(q).equals(x);
 
-                    return new ECFieldElement.Fp(q, V);
+                    return new ECFieldElement.Fp(q, r, V);
                 }
             }
             while (U.equals(ECConstants.ONE) || U.equals(qMinusOne));
@@ -230,8 +335,7 @@
 //            return r.multiply(r).multiply(x.modPow(q.subtract(ECConstants.TWO), q)).subtract(ECConstants.TWO).mod(p);
 //        }
 
-        private static BigInteger[] lucasSequence(
-            BigInteger  p,
+        private BigInteger[] lucasSequence(
             BigInteger  P,
             BigInteger  Q,
             BigInteger  k)
@@ -247,40 +351,122 @@
 
             for (int j = n - 1; j >= s + 1; --j)
             {
-                Ql = Ql.multiply(Qh).mod(p);
+                Ql = modMult(Ql, Qh);
 
                 if (k.testBit(j))
                 {
-                    Qh = Ql.multiply(Q).mod(p);
-                    Uh = Uh.multiply(Vh).mod(p);
-                    Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
-                    Vh = Vh.multiply(Vh).subtract(Qh.shiftLeft(1)).mod(p);
+                    Qh = modMult(Ql, Q);
+                    Uh = modMult(Uh, Vh);
+                    Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+                    Vh = modReduce(Vh.multiply(Vh).subtract(Qh.shiftLeft(1)));
                 }
                 else
                 {
                     Qh = Ql;
-                    Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
-                    Vh = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
-                    Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
+                    Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
+                    Vh = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+                    Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
                 }
             }
 
-            Ql = Ql.multiply(Qh).mod(p);
-            Qh = Ql.multiply(Q).mod(p);
-            Uh = Uh.multiply(Vl).subtract(Ql).mod(p);
-            Vl = Vh.multiply(Vl).subtract(P.multiply(Ql)).mod(p);
-            Ql = Ql.multiply(Qh).mod(p);
+            Ql = modMult(Ql, Qh);
+            Qh = modMult(Ql, Q);
+            Uh = modReduce(Uh.multiply(Vl).subtract(Ql));
+            Vl = modReduce(Vh.multiply(Vl).subtract(P.multiply(Ql)));
+            Ql = modMult(Ql, Qh);
 
             for (int j = 1; j <= s; ++j)
             {
-                Uh = Uh.multiply(Vl).mod(p);
-                Vl = Vl.multiply(Vl).subtract(Ql.shiftLeft(1)).mod(p);
-                Ql = Ql.multiply(Ql).mod(p);
+                Uh = modMult(Uh, Vl);
+                Vl = modReduce(Vl.multiply(Vl).subtract(Ql.shiftLeft(1)));
+                Ql = modMult(Ql, Ql);
             }
 
             return new BigInteger[]{ Uh, Vl };
         }
-        
+
+        protected BigInteger modAdd(BigInteger x1, BigInteger x2)
+        {
+            BigInteger x3 = x1.add(x2);
+            if (x3.compareTo(q) >= 0)
+            {
+                x3 = x3.subtract(q);
+            }
+            return x3;
+        }
+
+        protected BigInteger modDouble(BigInteger x)
+        {
+            BigInteger _2x = x.shiftLeft(1);
+            if (_2x.compareTo(q) >= 0)
+            {
+                _2x = _2x.subtract(q);
+            }
+            return _2x;
+        }
+
+        protected BigInteger modMult(BigInteger x1, BigInteger x2)
+        {
+            return modReduce(x1.multiply(x2));
+        }
+
+        protected BigInteger modReduce(BigInteger x)
+        {
+//            if (naf != null)
+//            {
+//                int last = naf.length - 1;
+//                int bits = naf[last];
+//                while (x.bitLength() > (bits + 1))
+//                {
+//                    BigInteger u = x.shiftRight(bits);
+//                    BigInteger v = x.subtract(u.shiftLeft(bits));
+//
+//                    x = v;
+//
+//                    for (int i = 0; i < last; ++i)
+//                    {
+//                        int ni = naf[i];
+//                        if (ni < 0)
+//                        {
+//                            x = x.add(u.shiftLeft(~ni));
+//                        }
+//                        else
+//                        {
+//                            x = x.subtract(u.shiftLeft(ni));
+//                        }
+//                    }
+//                }
+//                while (x.compareTo(q) >= 0)
+//                {
+//                    x = x.subtract(q);
+//                }
+//            }
+//            else
+            if (r != null)
+            {
+                int qLen = q.bitLength();
+                while (x.bitLength() > (qLen + 1))
+                {
+                    BigInteger u = x.shiftRight(qLen);
+                    BigInteger v = x.subtract(u.shiftLeft(qLen));
+                    if (!r.equals(ONE))
+                    {
+                        u = u.multiply(r);
+                    }
+                    x = u.add(v); 
+                }
+                while (x.compareTo(q) >= 0)
+                {
+                    x = x.subtract(q);
+                }
+            }
+            else
+            {
+                x = x.mod(q);
+            }
+            return x;
+        }
+
         public boolean equals(Object other)
         {
             if (other == this)
@@ -669,7 +855,7 @@
 //                g1z = g1z.xor(g2z.shiftLeft(j));
 ////                if (g1z.bitLength() > this.m) {
 ////                    throw new ArithmeticException(
-////                            "deg(g1z) >= m, g1z = " + g1z.toString(2));
+////                            "deg(g1z) >= m, g1z = " + g1z.toString(16));
 ////                }
 //            }
 //            return new ECFieldElement.F2m(
@@ -801,41 +987,38 @@
          */
         private int m;
 
-        /**
-         * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
-         * x<sup>k</sup> + 1</code> represents the reduction polynomial
-         * <code>f(z)</code>.<br>
-         * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.<br>
-         */
-        private int k1;
+//        /**
+//         * TPB: The integer <code>k</code> where <code>x<sup>m</sup> +
+//         * x<sup>k</sup> + 1</code> represents the reduction polynomial
+//         * <code>f(z)</code>.<br>
+//         * PPB: The integer <code>k1</code> where <code>x<sup>m</sup> +
+//         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+//         * represents the reduction polynomial <code>f(z)</code>.<br>
+//         */
+//        private int k1;
+//
+//        /**
+//         * TPB: Always set to <code>0</code><br>
+//         * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
+//         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+//         * represents the reduction polynomial <code>f(z)</code>.<br>
+//         */
+//        private int k2;
+//
+//        /**
+//         * TPB: Always set to <code>0</code><br>
+//         * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
+//         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
+//         * represents the reduction polynomial <code>f(z)</code>.<br>
+//         */
+//        private int k3;
+
+        private int[] ks;
 
         /**
-         * TPB: Always set to <code>0</code><br>
-         * PPB: The integer <code>k2</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.<br>
+         * The <code>LongArray</code> holding the bits.
          */
-        private int k2;
-
-        /**
-         * TPB: Always set to <code>0</code><br>
-         * PPB: The integer <code>k3</code> where <code>x<sup>m</sup> +
-         * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
-         * represents the reduction polynomial <code>f(z)</code>.<br>
-         */
-        private int k3;
-
-        /**
-         * The <code>IntArray</code> holding the bits.
-         */
-        private IntArray x;
-
-        /**
-         * The number of <code>int</code>s required to hold <code>m</code> bits.
-         */
-        private int t;
+        private LongArray x;
 
         /**
          * Constructor for PPB.
@@ -851,6 +1034,7 @@
          * x<sup>k3</sup> + x<sup>k2</sup> + x<sup>k1</sup> + 1</code>
          * represents the reduction polynomial <code>f(z)</code>.
          * @param x The BigInteger representing the value of the field element.
+         * @deprecated Use ECCurve.fromBigInteger to construct field elements
          */
         public F2m(
             int m, 
@@ -859,13 +1043,10 @@
             int k3,
             BigInteger x)
         {
-            // t = m / 32 rounded up to the next integer
-            t = (m + 31) >> 5;
-            this.x = new IntArray(x, t);
-
             if ((k2 == 0) && (k3 == 0))
             {
                 this.representation = TPB;
+                this.ks = new int[]{ k1 }; 
             }
             else
             {
@@ -880,17 +1061,11 @@
                             "k2 must be larger than 0");
                 }
                 this.representation = PPB;
-            }
-
-            if (x.signum() < 0)
-            {
-                throw new IllegalArgumentException("x value cannot be negative");
+                this.ks = new int[]{ k1, k2, k3 }; 
             }
 
             this.m = m;
-            this.k1 = k1;
-            this.k2 = k2;
-            this.k3 = k3;
+            this.x = new LongArray(x);
         }
 
         /**
@@ -901,6 +1076,7 @@
          * x<sup>k</sup> + 1</code> represents the reduction
          * polynomial <code>f(z)</code>.
          * @param x The BigInteger representing the value of the field element.
+         * @deprecated Use ECCurve.fromBigInteger to construct field elements
          */
         public F2m(int m, int k, BigInteger x)
         {
@@ -908,24 +1084,27 @@
             this(m, k, 0, 0, x);
         }
 
-        private F2m(int m, int k1, int k2, int k3, IntArray x)
+        private F2m(int m, int[] ks, LongArray x)
         {
-            t = (m + 31) >> 5;
-            this.x = x;
             this.m = m;
-            this.k1 = k1;
-            this.k2 = k2;
-            this.k3 = k3;
+            this.representation = (ks.length == 1) ? TPB : PPB;
+            this.ks = ks;
+            this.x = x;
+        }
 
-            if ((k2 == 0) && (k3 == 0))
-            {
-                this.representation = TPB;
-            }
-            else
-            {
-                this.representation = PPB;
-            }
+        public int bitLength()
+        {
+            return x.degree();
+        }
 
+        public boolean isZero()
+        {
+            return x.isZero();
+        }
+
+        public boolean testBitZero()
+        {
+            return x.testBitZero();
         }
 
         public BigInteger toBigInteger()
@@ -967,19 +1146,15 @@
             ECFieldElement.F2m aF2m = (ECFieldElement.F2m)a;
             ECFieldElement.F2m bF2m = (ECFieldElement.F2m)b;
 
-            if ((aF2m.m != bF2m.m) || (aF2m.k1 != bF2m.k1)
-                    || (aF2m.k2 != bF2m.k2) || (aF2m.k3 != bF2m.k3))
-            {
-                throw new IllegalArgumentException("Field elements are not "
-                        + "elements of the same field F2m");
-            }
-
             if (aF2m.representation != bF2m.representation)
             {
                 // Should never occur
-                throw new IllegalArgumentException(
-                        "One of the field "
-                                + "elements are not elements has incorrect representation");
+                throw new IllegalArgumentException("One of the F2m field elements has incorrect representation");
+            }
+
+            if ((aF2m.m != bF2m.m) || !Arrays.areEqual(aF2m.ks, bF2m.ks))
+            {
+                throw new IllegalArgumentException("Field elements are not elements of the same field F2m");
             }
         }
 
@@ -988,10 +1163,15 @@
             // No check performed here for performance reasons. Instead the
             // elements involved are checked in ECPoint.F2m
             // checkFieldElements(this, b);
-            IntArray iarrClone = (IntArray)this.x.clone();
+            LongArray iarrClone = (LongArray)this.x.clone();
             F2m bF2m = (F2m)b;
-            iarrClone.addShifted(bF2m.x, 0);
-            return new F2m(m, k1, k2, k3, iarrClone);
+            iarrClone.addShiftedByWords(bF2m.x, 0);
+            return new F2m(m, ks, iarrClone);
+        }
+
+        public ECFieldElement addOne()
+        {
+            return new F2m(m, ks, x.addOne());
         }
 
         public ECFieldElement subtract(final ECFieldElement b)
@@ -1002,17 +1182,14 @@
 
         public ECFieldElement multiply(final ECFieldElement b)
         {
-            // Right-to-left comb multiplication in the IntArray
+            // Right-to-left comb multiplication in the LongArray
             // Input: Binary polynomials a(z) and b(z) of degree at most m-1
             // Output: c(z) = a(z) * b(z) mod f(z)
 
             // No check performed here for performance reasons. Instead the
             // elements involved are checked in ECPoint.F2m
             // checkFieldElements(this, b);
-            F2m bF2m = (F2m)b;
-            IntArray mult = x.multiply(bF2m.x, m);
-            mult.reduce(m, new int[]{k1, k2, k3});
-            return new F2m(m, k1, k2, k3, mult);
+            return new F2m(m, ks, x.modMultiply(((F2m)b).x, m, ks));
         }
 
         public ECFieldElement divide(final ECFieldElement b)
@@ -1030,80 +1207,12 @@
 
         public ECFieldElement square()
         {
-            IntArray squared = x.square(m);
-            squared.reduce(m, new int[]{k1, k2, k3});
-            return new F2m(m, k1, k2, k3, squared);
+            return new F2m(m, ks, x.modSquare(m, ks));
         }
 
-
         public ECFieldElement invert()
         {
-            // Inversion in F2m using the extended Euclidean algorithm
-            // Input: A nonzero polynomial a(z) of degree at most m-1
-            // Output: a(z)^(-1) mod f(z)
-
-            // u(z) := a(z)
-            IntArray uz = (IntArray)this.x.clone();
-
-            // v(z) := f(z)
-            IntArray vz = new IntArray(t);
-            vz.setBit(m);
-            vz.setBit(0);
-            vz.setBit(this.k1);
-            if (this.representation == PPB) 
-            {
-                vz.setBit(this.k2);
-                vz.setBit(this.k3);
-            }
-
-            // g1(z) := 1, g2(z) := 0
-            IntArray g1z = new IntArray(t);
-            g1z.setBit(0);
-            IntArray g2z = new IntArray(t);
-
-            // while u != 0
-            while (!uz.isZero())
-//            while (uz.getUsedLength() > 0)
-//            while (uz.bitLength() > 1)
-            {
-                // j := deg(u(z)) - deg(v(z))
-                int j = uz.bitLength() - vz.bitLength();
-
-                // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
-                if (j < 0) 
-                {
-                    final IntArray uzCopy = uz;
-                    uz = vz;
-                    vz = uzCopy;
-
-                    final IntArray g1zCopy = g1z;
-                    g1z = g2z;
-                    g2z = g1zCopy;
-
-                    j = -j;
-                }
-
-                // u(z) := u(z) + z^j * v(z)
-                // Note, that no reduction modulo f(z) is required, because
-                // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
-                // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
-                // = deg(u(z))
-                // uz = uz.xor(vz.shiftLeft(j));
-                // jInt = n / 32
-                int jInt = j >> 5;
-                // jInt = n % 32
-                int jBit = j & 0x1F;
-                IntArray vzShift = vz.shiftLeft(jBit);
-                uz.addShifted(vzShift, jInt);
-
-                // g1(z) := g1(z) + z^j * g2(z)
-//                g1z = g1z.xor(g2z.shiftLeft(j));
-                IntArray g2zShift = g2z.shiftLeft(jBit);
-                g1z.addShifted(g2zShift, jInt);
-                
-            }
-            return new ECFieldElement.F2m(
-                    this.m, this.k1, this.k2, this.k3, g2z);
+            return new ECFieldElement.F2m(this.m, this.ks, this.x.modInverse(m, ks));
         }
 
         public ECFieldElement sqrt()
@@ -1143,7 +1252,7 @@
          */
         public int getK1()
         {
-            return this.k1;
+            return this.ks[0];
         }
 
         /**
@@ -1154,7 +1263,7 @@
          */
         public int getK2()
         {
-            return this.k2;
+            return this.ks.length >= 2 ? this.ks[1] : 0;
         }
 
         /**
@@ -1165,7 +1274,7 @@
          */
         public int getK3()
         {
-            return this.k3;
+            return this.ks.length >= 3 ? this.ks[2] : 0;
         }
 
         public boolean equals(Object anObject)
@@ -1182,15 +1291,15 @@
 
             ECFieldElement.F2m b = (ECFieldElement.F2m)anObject;
             
-            return ((this.m == b.m) && (this.k1 == b.k1) && (this.k2 == b.k2)
-                && (this.k3 == b.k3)
+            return ((this.m == b.m)
                 && (this.representation == b.representation)
+                && Arrays.areEqual(this.ks, b.ks)
                 && (this.x.equals(b.x)));
         }
 
         public int hashCode()
         {
-            return x.hashCode() ^ m ^ k1 ^ k2 ^ k3;
+            return x.hashCode() ^ m ^ Arrays.hashCode(ks);
         }
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java
index 4d72e33..da1013a 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECMultiplier.java
@@ -6,14 +6,14 @@
  * Interface for classes encapsulating a point multiplication algorithm
  * for <code>ECPoint</code>s.
  */
-interface ECMultiplier
+public interface ECMultiplier
 {
     /**
      * Multiplies the <code>ECPoint p</code> by <code>k</code>, i.e.
      * <code>p</code> is added <code>k</code> times to itself.
      * @param p The <code>ECPoint</code> to be multiplied.
-     * @param k The factor by which <code>p</code> i multiplied.
+     * @param k The factor by which <code>p</code> is multiplied.
      * @return <code>p</code> multiplied by <code>k</code>.
      */
-    ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo);
+    ECPoint multiply(ECPoint p, BigInteger k);
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java
index cbc5aaf..7f740e4 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ECPoint.java
@@ -2,59 +2,324 @@
 
 import java.math.BigInteger;
 
-import org.bouncycastle.asn1.x9.X9IntegerConverter;
-
 /**
  * base class for points on elliptic curves.
  */
 public abstract class ECPoint
 {
-    ECCurve        curve;
-    ECFieldElement x;
-    ECFieldElement y;
+    protected static ECFieldElement[] EMPTY_ZS = new ECFieldElement[0];
+
+    protected static ECFieldElement[] getInitialZCoords(ECCurve curve)
+    {
+        // Cope with null curve, most commonly used by implicitlyCa
+        int coord = null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem();
+
+        switch (coord)
+        {
+        case ECCurve.COORD_AFFINE:
+        case ECCurve.COORD_LAMBDA_AFFINE:
+            return EMPTY_ZS;
+        default:
+            break;
+        }
+
+        ECFieldElement one = curve.fromBigInteger(ECConstants.ONE);
+
+        switch (coord)
+        {
+        case ECCurve.COORD_HOMOGENEOUS:
+        case ECCurve.COORD_JACOBIAN:
+        case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            return new ECFieldElement[]{ one };
+        case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+            return new ECFieldElement[]{ one, one, one };
+        case ECCurve.COORD_JACOBIAN_MODIFIED:
+            return new ECFieldElement[]{ one, curve.getA() };
+        default:
+            throw new IllegalArgumentException("unknown coordinate system");
+        }
+    }
+
+    protected ECCurve curve;
+    protected ECFieldElement x;
+    protected ECFieldElement y;
+    protected ECFieldElement[] zs;
 
     protected boolean withCompression;
 
-    protected ECMultiplier multiplier = null;
-
     protected PreCompInfo preCompInfo = null;
 
-    private static X9IntegerConverter converter = new X9IntegerConverter();
-
     protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y)
     {
+        this(curve, x, y, getInitialZCoords(curve));
+    }
+
+    protected ECPoint(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs)
+    {
         this.curve = curve;
         this.x = x;
         this.y = y;
+        this.zs = zs;
     }
-    
+
     public ECCurve getCurve()
     {
         return curve;
     }
-    
+
+    protected int getCurveCoordinateSystem()
+    {
+        // Cope with null curve, most commonly used by implicitlyCa
+        return null == curve ? ECCurve.COORD_AFFINE : curve.getCoordinateSystem();
+    }
+
+    /**
+     * Normalizes this point, and then returns the affine x-coordinate.
+     * 
+     * Note: normalization can be expensive, this method is deprecated in favour
+     * of caller-controlled normalization.
+     * 
+     * @deprecated Use getAffineXCoord, or normalize() and getXCoord(), instead
+     */
     public ECFieldElement getX()
     {
+        return normalize().getXCoord();
+    }
+
+
+    /**
+     * Normalizes this point, and then returns the affine y-coordinate.
+     * 
+     * Note: normalization can be expensive, this method is deprecated in favour
+     * of caller-controlled normalization.
+     * 
+     * @deprecated Use getAffineYCoord, or normalize() and getYCoord(), instead
+     */
+    public ECFieldElement getY()
+    {
+        return normalize().getYCoord();
+    }
+
+    /**
+     * Returns the affine x-coordinate after checking that this point is normalized.
+     * 
+     * @return The affine x-coordinate of this point
+     * @throws IllegalStateException if the point is not normalized
+     */
+    public ECFieldElement getAffineXCoord()
+    {
+        checkNormalized();
+        return getXCoord();
+    }
+
+    /**
+     * Returns the affine y-coordinate after checking that this point is normalized
+     * 
+     * @return The affine y-coordinate of this point
+     * @throws IllegalStateException if the point is not normalized
+     */
+    public ECFieldElement getAffineYCoord()
+    {
+        checkNormalized();
+        return getYCoord();
+    }
+
+    /**
+     * Returns the x-coordinate.
+     * 
+     * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+     * affine coordinate system; use normalize() to get a point where the coordinates have their
+     * affine values, or use getAffineXCoord if you expect the point to already have been
+     * normalized.
+     * 
+     * @return the x-coordinate of this point
+     */
+    public ECFieldElement getXCoord()
+    {
         return x;
     }
 
-    public ECFieldElement getY()
+    /**
+     * Returns the y-coordinate.
+     * 
+     * Caution: depending on the curve's coordinate system, this may not be the same value as in an
+     * affine coordinate system; use normalize() to get a point where the coordinates have their
+     * affine values, or use getAffineYCoord if you expect the point to already have been
+     * normalized.
+     * 
+     * @return the y-coordinate of this point
+     */
+    public ECFieldElement getYCoord()
     {
         return y;
     }
 
+    public ECFieldElement getZCoord(int index)
+    {
+        return (index < 0 || index >= zs.length) ? null : zs[index];
+    }
+
+    public ECFieldElement[] getZCoords()
+    {
+        int zsLen = zs.length;
+        if (zsLen == 0)
+        {
+            return zs;
+        }
+        ECFieldElement[] copy = new ECFieldElement[zsLen];
+        System.arraycopy(zs, 0, copy, 0, zsLen);
+        return copy;
+    }
+
+    protected ECFieldElement getRawXCoord()
+    {
+        return x;
+    }
+
+    protected ECFieldElement getRawYCoord()
+    {
+        return y;
+    }
+
+    protected void checkNormalized()
+    {
+        if (!isNormalized())
+        {
+            throw new IllegalStateException("point not in normal form");
+        }
+    }
+
+    public boolean isNormalized()
+    {
+        int coord = this.getCurveCoordinateSystem();
+
+        return coord == ECCurve.COORD_AFFINE
+            || coord == ECCurve.COORD_LAMBDA_AFFINE
+            || isInfinity()
+            || zs[0].bitLength() == 1;
+    }
+
+    /**
+     * Normalization ensures that any projective coordinate is 1, and therefore that the x, y
+     * coordinates reflect those of the equivalent point in an affine coordinate system.
+     * 
+     * @return a new ECPoint instance representing the same point, but with normalized coordinates
+     */
+    public ECPoint normalize()
+    {
+        if (this.isInfinity())
+        {
+            return this;
+        }
+
+        switch (this.getCurveCoordinateSystem())
+        {
+        case ECCurve.COORD_AFFINE:
+        case ECCurve.COORD_LAMBDA_AFFINE:
+        {
+            return this;
+        }
+        default:
+        {
+            ECFieldElement Z1 = getZCoord(0);
+            if (Z1.bitLength() == 1)
+            {
+                return this;
+            }
+
+            return normalize(Z1.invert());
+        }
+        }
+    }
+
+    ECPoint normalize(ECFieldElement zInv)
+    {
+        switch (this.getCurveCoordinateSystem())
+        {
+        case ECCurve.COORD_HOMOGENEOUS:
+        case ECCurve.COORD_LAMBDA_PROJECTIVE:
+        {
+            return createScaledPoint(zInv, zInv);
+        }
+        case ECCurve.COORD_JACOBIAN:
+        case ECCurve.COORD_JACOBIAN_CHUDNOVSKY:
+        case ECCurve.COORD_JACOBIAN_MODIFIED:
+        {
+            ECFieldElement zInv2 = zInv.square(), zInv3 = zInv2.multiply(zInv);
+            return createScaledPoint(zInv2, zInv3);
+        }
+        default:
+        {
+            throw new IllegalStateException("not a projective coordinate system");
+        }
+        }
+    }
+
+    protected ECPoint createScaledPoint(ECFieldElement sx, ECFieldElement sy)
+    {
+        return this.getCurve().createRawPoint(getRawXCoord().multiply(sx), getRawYCoord().multiply(sy), this.withCompression);
+    }
+
     public boolean isInfinity()
     {
-        return x == null && y == null;
+        return x == null || y == null || (zs.length > 0 && zs[0].isZero());
     }
 
     public boolean isCompressed()
     {
-        return withCompression;
+        return this.withCompression;
     }
 
-    public boolean equals(
-        Object  other)
+    public boolean equals(ECPoint other)
+    {
+        if (null == other)
+        {
+            return false;
+        }
+
+        ECCurve c1 = this.getCurve(), c2 = other.getCurve();
+        boolean n1 = (null == c1), n2 = (null == c2);
+        boolean i1 = isInfinity(), i2 = other.isInfinity();
+
+        if (i1 || i2)
+        {
+            return (i1 && i2) && (n1 || n2 || c1.equals(c2));
+        }
+
+        ECPoint p1 = this, p2 = other;
+        if (n1 && n2)
+        {
+            // Points with null curve are in affine form, so already normalized
+        }
+        else if (n1)
+        {
+            p2 = p2.normalize();
+        }
+        else if (n2)
+        {
+            p1 = p1.normalize();
+        }
+        else if (!c1.equals(c2))
+        {
+            return false;
+        }
+        else
+        {
+            // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+            ECPoint[] points = new ECPoint[]{ this, c1.importPoint(p2) };
+
+            // TODO This is a little strong, really only requires coZNormalizeAll to get Zs equal
+            c1.normalizeAll(points);
+
+            p1 = points[0];
+            p2 = points[1];
+        }
+
+        return p1.getXCoord().equals(p2.getXCoord()) && p1.getYCoord().equals(p2.getYCoord());
+    }
+
+    public boolean equals(Object other)
     {
         if (other == this)
         {
@@ -66,69 +331,117 @@
             return false;
         }
 
-        ECPoint o = (ECPoint)other;
-
-        if (this.isInfinity())
-        {
-            return o.isInfinity();
-        }
-
-        return x.equals(o.x) && y.equals(o.y);
+        return equals((ECPoint)other);
     }
 
     public int hashCode()
     {
-        if (this.isInfinity())
+        ECCurve c = this.getCurve();
+        int hc = (null == c) ? 0 : ~c.hashCode();
+
+        if (!this.isInfinity())
         {
-            return 0;
+            // TODO Consider just requiring already normalized, to avoid silent performance degradation
+
+            ECPoint p = normalize();
+
+            hc ^= p.getXCoord().hashCode() * 17;
+            hc ^= p.getYCoord().hashCode() * 257;
         }
-        
-        return x.hashCode() ^ y.hashCode();
+
+        return hc;
     }
 
-//    /**
-//     * Mainly for testing. Explicitly set the <code>ECMultiplier</code>.
-//     * @param multiplier The <code>ECMultiplier</code> to be used to multiply
-//     * this <code>ECPoint</code>.
-//     */
-//    public void setECMultiplier(ECMultiplier multiplier)
-//    {
-//        this.multiplier = multiplier;
-//    }
-
-    /**
-     * Sets the <code>PreCompInfo</code>. Used by <code>ECMultiplier</code>s
-     * to save the precomputation for this <code>ECPoint</code> to store the
-     * precomputation result for use by subsequent multiplication.
-     * @param preCompInfo The values precomputed by the
-     * <code>ECMultiplier</code>.
-     */
-    void setPreCompInfo(PreCompInfo preCompInfo)
+    public String toString()
     {
-        this.preCompInfo = preCompInfo;
+        if (this.isInfinity())
+        {
+            return "INF";
+        }
+
+        StringBuffer sb = new StringBuffer();
+        sb.append('(');
+        sb.append(getRawXCoord());
+        sb.append(',');
+        sb.append(getRawYCoord());
+        for (int i = 0; i < zs.length; ++i)
+        {
+            sb.append(',');
+            sb.append(zs[i]);
+        }
+        sb.append(')');
+        return sb.toString();
     }
 
     public byte[] getEncoded()
     {
-        return getEncoded(withCompression);
+        return getEncoded(this.withCompression);
     }
 
-    public abstract byte[] getEncoded(boolean compressed);
+    /**
+     * return the field element encoded with point compression. (S 4.3.6)
+     */
+    public byte[] getEncoded(boolean compressed)
+    {
+        if (this.isInfinity())
+        {
+            return new byte[1];
+        }
+
+        ECPoint normed = normalize();
+
+        byte[] X = normed.getXCoord().getEncoded();
+
+        if (compressed)
+        {
+            byte[] PO = new byte[X.length + 1];
+            PO[0] = (byte)(normed.getCompressionYTilde() ? 0x03 : 0x02);
+            System.arraycopy(X, 0, PO, 1, X.length);
+            return PO;
+        }
+
+        byte[] Y = normed.getYCoord().getEncoded();
+
+        byte[] PO = new byte[X.length + Y.length + 1];
+        PO[0] = 0x04;
+        System.arraycopy(X, 0, PO, 1, X.length);
+        System.arraycopy(Y, 0, PO, X.length + 1, Y.length);
+        return PO;
+    }
+
+    protected abstract boolean getCompressionYTilde();
 
     public abstract ECPoint add(ECPoint b);
-    public abstract ECPoint subtract(ECPoint b);
+
     public abstract ECPoint negate();
+
+    public abstract ECPoint subtract(ECPoint b);
+
+    public ECPoint timesPow2(int e)
+    {
+        if (e < 0)
+        {
+            throw new IllegalArgumentException("'e' cannot be negative");
+        }
+
+        ECPoint p = this;
+        while (--e >= 0)
+        {
+            p = p.twice();
+        }
+        return p;
+    }
+
     public abstract ECPoint twice();
 
-    /**
-     * Sets the default <code>ECMultiplier</code>, unless already set. 
-     */
-    synchronized void assertECMultiplier()
+    public ECPoint twicePlus(ECPoint b)
     {
-        if (this.multiplier == null)
-        {
-            this.multiplier = new FpNafMultiplier();
-        }
+        return twice().add(b);
+    }
+
+    public ECPoint threeTimes()
+    {
+        return twicePlus(this);
     }
 
     /**
@@ -138,23 +451,7 @@
      */
     public ECPoint multiply(BigInteger k)
     {
-        if (k.signum() < 0)
-        {
-            throw new IllegalArgumentException("The multiplicator cannot be negative");
-        }
-
-        if (this.isInfinity())
-        {
-            return this;
-        }
-
-        if (k.signum() == 0)
-        {
-            return this.curve.getInfinity();
-        }
-
-        assertECMultiplier();
-        return this.multiplier.multiply(this, k, preCompInfo);
+        return this.getCurve().getMultiplier().multiply(this, k);
     }
 
     /**
@@ -162,13 +459,14 @@
      */
     public static class Fp extends ECPoint
     {
-        
         /**
          * Create a point which encodes with point compression.
          * 
          * @param curve the curve to use
          * @param x affine x co-ordinate
          * @param y affine y co-ordinate
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
          */
         public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y)
         {
@@ -182,6 +480,8 @@
          * @param x affine x co-ordinate
          * @param y affine y co-ordinate
          * @param withCompression if true encode with point compression
+         * 
+         * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
          */
         public Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
         {
@@ -194,52 +494,27 @@
 
             this.withCompression = withCompression;
         }
-         
-        /**
-         * return the field element encoded with point compression. (S 4.3.6)
-         */
-        public byte[] getEncoded(boolean compressed)
+
+        Fp(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
         {
-            if (this.isInfinity()) 
+            super(curve, x, y, zs);
+
+            this.withCompression = withCompression;
+        }
+
+        protected boolean getCompressionYTilde()
+        {
+            return this.getAffineYCoord().testBitZero();
+        }
+
+        public ECFieldElement getZCoord(int index)
+        {
+            if (index == 1 && ECCurve.COORD_JACOBIAN_MODIFIED == this.getCurveCoordinateSystem())
             {
-                return new byte[1];
+                return getJacobianModifiedW();
             }
 
-            int qLength = converter.getByteLength(x);
-            
-            if (compressed)
-            {
-                byte    PC;
-    
-                if (this.getY().toBigInteger().testBit(0))
-                {
-                    PC = 0x03;
-                }
-                else
-                {
-                    PC = 0x02;
-                }
-    
-                byte[]  X = converter.integerToBytes(this.getX().toBigInteger(), qLength);
-                byte[]  PO = new byte[X.length + 1];
-    
-                PO[0] = PC;
-                System.arraycopy(X, 0, PO, 1, X.length);
-    
-                return PO;
-            }
-            else
-            {
-                byte[]  X = converter.integerToBytes(this.getX().toBigInteger(), qLength);
-                byte[]  Y = converter.integerToBytes(this.getY().toBigInteger(), qLength);
-                byte[]  PO = new byte[X.length + Y.length + 1];
-                
-                PO[0] = 0x04;
-                System.arraycopy(X, 0, PO, 1, X.length);
-                System.arraycopy(Y, 0, PO, X.length + 1, Y.length);
-
-                return PO;
-            }
+            return super.getZCoord(index);
         }
 
         // B.3 pg 62
@@ -249,31 +524,222 @@
             {
                 return b;
             }
-
             if (b.isInfinity())
             {
                 return this;
             }
-
-            // Check if b = this or b = -this
-            if (this.x.equals(b.x))
+            if (this == b)
             {
-                if (this.y.equals(b.y))
-                {
-                    // this = b, i.e. this must be doubled
-                    return this.twice();
-                }
-
-                // this = -b, i.e. the result is the point at infinity
-                return this.curve.getInfinity();
+                return twice();
             }
 
-            ECFieldElement gamma = b.y.subtract(this.y).divide(b.x.subtract(this.x));
+            ECCurve curve = this.getCurve();
+            int coord = curve.getCoordinateSystem();
 
-            ECFieldElement x3 = gamma.square().subtract(this.x).subtract(b.x);
-            ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
+            ECFieldElement X1 = this.x, Y1 = this.y;
+            ECFieldElement X2 = b.x, Y2 = b.y;
 
-            return new ECPoint.Fp(curve, x3, y3, withCompression);
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1);
+
+                if (dx.isZero())
+                {
+                    if (dy.isZero())
+                    {
+                        // this == b, i.e. this must be doubled
+                        return twice();
+                    }
+
+                    // this == -b, i.e. the result is the point at infinity
+                    return curve.getInfinity();
+                }
+
+                ECFieldElement gamma = dy.divide(dx);
+                ECFieldElement X3 = gamma.square().subtract(X1).subtract(X2);
+                ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1);
+
+                return new ECPoint.Fp(curve, X3, Y3, this.withCompression);
+            }
+
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Z1 = this.zs[0];
+                ECFieldElement Z2 = b.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+                boolean Z2IsOne = Z2.bitLength() == 1;
+
+                ECFieldElement u1 = Z1IsOne ? Y2 : Y2.multiply(Z1);
+                ECFieldElement u2 = Z2IsOne ? Y1 : Y1.multiply(Z2);
+                ECFieldElement u = u1.subtract(u2);
+                ECFieldElement v1 = Z1IsOne ? X2 : X2.multiply(Z1);
+                ECFieldElement v2 = Z2IsOne ? X1 : X1.multiply(Z2);
+                ECFieldElement v = v1.subtract(v2);
+
+                // Check if b == this or b == -this
+                if (v.isZero())
+                {
+                    if (u.isZero())
+                    {
+                        // this == b, i.e. this must be doubled
+                        return this.twice();
+                    }
+
+                    // this == -b, i.e. the result is the point at infinity
+                    return curve.getInfinity();
+                }
+
+                // TODO Optimize for when w == 1
+                ECFieldElement w = Z1IsOne ? Z2 : Z2IsOne ? Z1 : Z1.multiply(Z2);
+                ECFieldElement vSquared = v.square();
+                ECFieldElement vCubed = vSquared.multiply(v);
+                ECFieldElement vSquaredV2 = vSquared.multiply(v2);
+                ECFieldElement A = u.square().multiply(w).subtract(vCubed).subtract(two(vSquaredV2));
+
+                ECFieldElement X3 = v.multiply(A);
+                ECFieldElement Y3 = vSquaredV2.subtract(A).multiply(u).subtract(vCubed.multiply(u2));
+                ECFieldElement Z3 = vCubed.multiply(w);
+
+                return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+
+            case ECCurve.COORD_JACOBIAN:
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+            {
+                ECFieldElement Z1 = this.zs[0];
+                ECFieldElement Z2 = b.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+
+                ECFieldElement X3, Y3, Z3, Z3Squared = null;
+
+                if (!Z1IsOne && Z1.equals(Z2))
+                {
+                    // TODO Make this available as public method coZAdd?
+
+                    ECFieldElement dx = X1.subtract(X2), dy = Y1.subtract(Y2);
+                    if (dx.isZero())
+                    {
+                        if (dy.isZero())
+                        {
+                            return twice();
+                        }
+                        return curve.getInfinity();
+                    }
+
+                    ECFieldElement C = dx.square();
+                    ECFieldElement W1 = X1.multiply(C), W2 = X2.multiply(C);
+                    ECFieldElement A1 = W1.subtract(W2).multiply(Y1);
+
+                    X3 = dy.square().subtract(W1).subtract(W2);
+                    Y3 = W1.subtract(X3).multiply(dy).subtract(A1);
+                    Z3 = dx;
+
+                    if (Z1IsOne)
+                    {
+                        Z3Squared = C;
+                    }
+                    else
+                    {
+                        Z3 = Z3.multiply(Z1);
+                    }
+                }
+                else
+                {
+                    ECFieldElement Z1Squared, U2, S2;
+                    if (Z1IsOne)
+                    {
+                        Z1Squared = Z1; U2 = X2; S2 = Y2;
+                    }
+                    else
+                    {
+                        Z1Squared = Z1.square();
+                        U2 = Z1Squared.multiply(X2);
+                        ECFieldElement Z1Cubed = Z1Squared.multiply(Z1);
+                        S2 = Z1Cubed.multiply(Y2);
+                    }
+
+                    boolean Z2IsOne = Z2.bitLength() == 1;
+                    ECFieldElement Z2Squared, U1, S1;
+                    if (Z2IsOne)
+                    {
+                        Z2Squared = Z2; U1 = X1; S1 = Y1;
+                    }
+                    else
+                    {
+                        Z2Squared = Z2.square();
+                        U1 = Z2Squared.multiply(X1); 
+                        ECFieldElement Z2Cubed = Z2Squared.multiply(Z2);
+                        S1 = Z2Cubed.multiply(Y1);
+                    }
+
+                    ECFieldElement H = U1.subtract(U2);
+                    ECFieldElement R = S1.subtract(S2);
+    
+                    // Check if b == this or b == -this
+                    if (H.isZero())
+                    {
+                        if (R.isZero())
+                        {
+                            // this == b, i.e. this must be doubled
+                            return this.twice();
+                        }
+    
+                        // this == -b, i.e. the result is the point at infinity
+                        return curve.getInfinity();
+                    }
+    
+                    ECFieldElement HSquared = H.square();
+                    ECFieldElement G = HSquared.multiply(H);
+                    ECFieldElement V = HSquared.multiply(U1);
+    
+                    X3 = R.square().add(G).subtract(two(V));
+                    Y3 = V.subtract(X3).multiply(R).subtract(S1.multiply(G));
+    
+                    Z3 = H;
+                    if (!Z1IsOne)
+                    {
+                        Z3 = Z3.multiply(Z1);
+                    }
+                    if (!Z2IsOne)
+                    {
+                        Z3 = Z3.multiply(Z2);
+                    }
+    
+                    // Alternative calculation of Z3 using fast square
+    //                X3 = four(X3);
+    //                Y3 = eight(Y3);
+    //                Z3 = doubleProductFromSquares(Z1, Z2, Z1Squared, Z2Squared).multiply(H);
+                    
+                    if (Z3 == H)
+                    {
+                        Z3Squared = HSquared;
+                    }
+                }
+
+                ECFieldElement[] zs;
+                if (coord == ECCurve.COORD_JACOBIAN_MODIFIED)
+                {
+                    // TODO If the result will only be used in a subsequent addition, we don't need W3
+                    ECFieldElement W3 = calculateJacobianModifiedW(Z3, Z3Squared);
+
+                    zs = new ECFieldElement[]{ Z3, W3 };
+                }
+                else
+                {
+                    zs = new ECFieldElement[]{ Z3 };
+                }
+
+                return new ECPoint.Fp(curve, X3, Y3, zs, this.withCompression);
+            }
+            default:
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
+            }
         }
 
         // B.3 pg 62
@@ -281,25 +747,284 @@
         {
             if (this.isInfinity())
             {
-                // Twice identity element (point at infinity) is identity
                 return this;
             }
 
-            if (this.y.toBigInteger().signum() == 0) 
+            ECCurve curve = this.getCurve();
+
+            ECFieldElement Y1 = this.y;
+            if (Y1.isZero()) 
             {
-                // if y1 == 0, then (x1, y1) == (x1, -y1)
-                // and hence this = -this and thus 2(x1, y1) == infinity
-                return this.curve.getInfinity();
+                return curve.getInfinity();
             }
 
-            ECFieldElement TWO = this.curve.fromBigInteger(BigInteger.valueOf(2));
-            ECFieldElement THREE = this.curve.fromBigInteger(BigInteger.valueOf(3));
-            ECFieldElement gamma = this.x.square().multiply(THREE).add(curve.a).divide(y.multiply(TWO));
+            int coord = curve.getCoordinateSystem();
 
-            ECFieldElement x3 = gamma.square().subtract(this.x.multiply(TWO));
-            ECFieldElement y3 = gamma.multiply(this.x.subtract(x3)).subtract(this.y);
+            ECFieldElement X1 = this.x;
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement X1Squared = X1.square();
+                ECFieldElement gamma = three(X1Squared).add(this.getCurve().getA()).divide(two(Y1));
+                ECFieldElement X3 = gamma.square().subtract(two(X1));
+                ECFieldElement Y3 = gamma.multiply(X1.subtract(X3)).subtract(Y1);
+    
+                return new ECPoint.Fp(curve, X3, Y3, this.withCompression);
+            }
+
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Z1 = this.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+                ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square();
+
+                // TODO Optimize for small negative a4 and -3
+                ECFieldElement w = curve.getA();
+                if (!Z1IsOne)
+                {
+                    w = w.multiply(Z1Squared);
+                }
+                w = w.add(three(X1.square()));
                 
-            return new ECPoint.Fp(curve, x3, y3, this.withCompression);
+                ECFieldElement s = Z1IsOne ? Y1 : Y1.multiply(Z1);
+                ECFieldElement t = Z1IsOne ? Y1.square() : s.multiply(Y1);
+                ECFieldElement B = X1.multiply(t);
+                ECFieldElement _4B = four(B);
+                ECFieldElement h = w.square().subtract(two(_4B));
+
+                ECFieldElement X3 = two(h.multiply(s));
+                ECFieldElement Y3 = w.multiply(_4B.subtract(h)).subtract(two(two(t).square()));
+                ECFieldElement _4sSquared = Z1IsOne ? four(t) : two(s).square();
+                ECFieldElement Z3 = two(_4sSquared).multiply(s);
+
+                return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+
+            case ECCurve.COORD_JACOBIAN:
+            {
+                ECFieldElement Z1 = this.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+                ECFieldElement Z1Squared = Z1IsOne ? Z1 : Z1.square();
+
+                ECFieldElement Y1Squared = Y1.square();
+                ECFieldElement T = Y1Squared.square();
+
+                ECFieldElement a4 = curve.getA();
+                ECFieldElement a4Neg = a4.negate();
+
+                ECFieldElement M, S;
+                if (a4Neg.toBigInteger().equals(BigInteger.valueOf(3)))
+                {
+                    M = three(X1.add(Z1Squared).multiply(X1.subtract(Z1Squared)));
+                    S = four(Y1Squared.multiply(X1));
+                }
+                else
+                {
+                    ECFieldElement X1Squared = X1.square();
+                    M = three(X1Squared);
+                    if (Z1IsOne)
+                    {
+                        M = M.add(a4);
+                    }
+                    else
+                    {
+                        ECFieldElement Z1Pow4 = Z1Squared.square();
+                        if (a4Neg.bitLength() < a4.bitLength())
+                        {
+                            M = M.subtract(Z1Pow4.multiply(a4Neg));
+                        }
+                        else
+                        {
+                            M = M.add(Z1Pow4.multiply(a4));
+                        }
+                    }
+                    S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+                }
+
+                ECFieldElement X3 = M.square().subtract(two(S));
+                ECFieldElement Y3 = S.subtract(X3).multiply(M).subtract(eight(T));
+
+                ECFieldElement Z3 = two(Y1);
+                if (!Z1IsOne)
+                {
+                    Z3 = Z3.multiply(Z1);
+                }
+
+                // Alternative calculation of Z3 using fast square
+//                ECFieldElement Z3 = doubleProductFromSquares(Y1, Z1, Y1Squared, Z1Squared);
+
+                return new ECPoint.Fp(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+            {
+                return twiceJacobianModified(true);
+            }
+
+            default:
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
+            }
+        }
+
+        public ECPoint twicePlus(ECPoint b)
+        {
+            if (this == b)
+            {
+                return threeTimes();
+            }
+            if (this.isInfinity())
+            {
+                return b;
+            }
+            if (b.isInfinity())
+            {
+                return twice();
+            }
+
+            ECFieldElement Y1 = this.y;
+            if (Y1.isZero()) 
+            {
+                return b;
+            }
+
+            ECCurve curve = this.getCurve();
+            int coord = curve.getCoordinateSystem();
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement X1 = this.x;
+                ECFieldElement X2 = b.x, Y2 = b.y;
+
+                ECFieldElement dx = X2.subtract(X1), dy = Y2.subtract(Y1);
+
+                if (dx.isZero())
+                {
+                    if (dy.isZero())
+                    {
+                        // this == b i.e. the result is 3P
+                        return threeTimes();
+                    }
+
+                    // this == -b, i.e. the result is P
+                    return this;
+                }
+
+                /*
+                 * Optimized calculation of 2P + Q, as described in "Trading Inversions for
+                 * Multiplications in Elliptic Curve Cryptography", by Ciet, Joye, Lauter, Montgomery.
+                 */
+
+                ECFieldElement X = dx.square(), Y = dy.square();
+                ECFieldElement d = X.multiply(two(X1).add(X2)).subtract(Y);
+                if (d.isZero())
+                {
+                    return curve.getInfinity();
+                }
+
+                ECFieldElement D = d.multiply(dx);
+                ECFieldElement I = D.invert();
+                ECFieldElement L1 = d.multiply(I).multiply(dy);
+                ECFieldElement L2 = two(Y1).multiply(X).multiply(dx).multiply(I).subtract(L1);
+                ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X2);
+                ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1);
+
+                return new ECPoint.Fp(curve, X4, Y4, this.withCompression);
+            }
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+            {
+                return twiceJacobianModified(false).add(b);
+            }
+            default:
+            {
+                return twice().add(b);
+            }
+            }
+        }
+
+        public ECPoint threeTimes()
+        {
+            if (this.isInfinity() || this.y.isZero())
+            {
+                return this;
+            }
+
+            ECCurve curve = this.getCurve();
+            int coord = curve.getCoordinateSystem();
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement X1 = this.x, Y1 = this.y;
+
+                ECFieldElement _2Y1 = two(Y1); 
+                ECFieldElement X = _2Y1.square();
+                ECFieldElement Z = three(X1.square()).add(this.getCurve().getA());
+                ECFieldElement Y = Z.square();
+
+                ECFieldElement d = three(X1).multiply(X).subtract(Y);
+                if (d.isZero())
+                {
+                    return this.getCurve().getInfinity();
+                }
+
+                ECFieldElement D = d.multiply(_2Y1); 
+                ECFieldElement I = D.invert();
+                ECFieldElement L1 = d.multiply(I).multiply(Z);
+                ECFieldElement L2 = X.square().multiply(I).subtract(L1);
+
+                ECFieldElement X4 = (L2.subtract(L1)).multiply(L1.add(L2)).add(X1);
+                ECFieldElement Y4 = (X1.subtract(X4)).multiply(L2).subtract(Y1); 
+                return new ECPoint.Fp(curve, X4, Y4, this.withCompression);
+            }
+            case ECCurve.COORD_JACOBIAN_MODIFIED:
+            {
+                return twiceJacobianModified(false).add(this);
+            }
+            default:
+            {
+                // NOTE: Be careful about recursions between twicePlus and threeTimes
+                return twice().add(this);
+            }
+            }
+        }
+
+        protected ECFieldElement two(ECFieldElement x)
+        {
+            return x.add(x);
+        }
+
+        protected ECFieldElement three(ECFieldElement x)
+        {
+            return two(x).add(x);
+        }
+
+        protected ECFieldElement four(ECFieldElement x)
+        {
+            return two(two(x));
+        }
+
+        protected ECFieldElement eight(ECFieldElement x)
+        {
+            return four(two(x));
+        }
+
+        protected ECFieldElement doubleProductFromSquares(ECFieldElement a, ECFieldElement b,
+            ECFieldElement aSquared, ECFieldElement bSquared)
+        {
+            /*
+             * NOTE: If squaring in the field is faster than multiplication, then this is a quicker
+             * way to calculate 2.A.B, if A^2 and B^2 are already known.
+             */
+            return a.add(b).square().subtract(aSquared).subtract(bSquared);
         }
 
         // D.3.2 pg 102 (see Note:)
@@ -316,18 +1041,70 @@
 
         public ECPoint negate()
         {
+            if (this.isInfinity())
+            {
+                return this;
+            }
+
+            ECCurve curve = this.getCurve();
+            int coord = curve.getCoordinateSystem();
+
+            if (ECCurve.COORD_AFFINE != coord)
+            {
+                return new ECPoint.Fp(curve, this.x, this.y.negate(), this.zs, this.withCompression);
+            }
+
             return new ECPoint.Fp(curve, this.x, this.y.negate(), this.withCompression);
         }
 
-        /**
-         * Sets the default <code>ECMultiplier</code>, unless already set. 
-         */
-        synchronized void assertECMultiplier()
+        protected ECFieldElement calculateJacobianModifiedW(ECFieldElement Z, ECFieldElement ZSquared)
         {
-            if (this.multiplier == null)
+            if (ZSquared == null)
             {
-                this.multiplier = new WNafMultiplier();
+                ZSquared = Z.square();
             }
+
+            ECFieldElement W = ZSquared.square();
+            ECFieldElement a4 = this.getCurve().getA();
+            ECFieldElement a4Neg = a4.negate();
+            if (a4Neg.bitLength() < a4.bitLength())
+            {
+                W = W.multiply(a4Neg).negate();
+            }
+            else
+            {
+                W = W.multiply(a4);
+            }
+            return W;
+        }
+
+        protected ECFieldElement getJacobianModifiedW()
+        {
+            ECFieldElement W = this.zs[1];
+            if (W == null)
+            {
+                // NOTE: Rarely, twicePlus will result in the need for a lazy W1 calculation here
+                this.zs[1] = W = calculateJacobianModifiedW(this.zs[0], null);
+            }
+            return W;
+        }
+
+        protected ECPoint.Fp twiceJacobianModified(boolean calculateW)
+        {
+            ECFieldElement X1 = this.x, Y1 = this.y, Z1 = this.zs[0], W1 = getJacobianModifiedW();
+
+            ECFieldElement X1Squared = X1.square();
+            ECFieldElement M = three(X1Squared).add(W1);
+            ECFieldElement Y1Squared = Y1.square();
+            ECFieldElement T = Y1Squared.square();
+            ECFieldElement S = two(doubleProductFromSquares(X1, Y1Squared, X1Squared, T));
+            ECFieldElement X3 = M.square().subtract(two(S));
+            ECFieldElement _8T = eight(T);
+            ECFieldElement Y3 = M.multiply(S.subtract(X3)).subtract(_8T);
+            ECFieldElement W3 = calculateW ? two(_8T.multiply(W1)) : null;
+            ECFieldElement Z3 = two(Z1.bitLength() == 1 ? Y1 : Y1.multiply(Z1));
+
+            return new ECPoint.Fp(this.getCurve(), X3, Y3, new ECFieldElement[]{ Z3, W3 }, this.withCompression);
         }
     }
 
@@ -340,6 +1117,8 @@
          * @param curve base curve
          * @param x x point
          * @param y y point
+         * 
+         * @deprecated Use ECCurve.createPoint to construct points
          */
         public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y)
         {
@@ -351,6 +1130,8 @@
          * @param x x point
          * @param y y point
          * @param withCompression true if encode with point compression.
+         * 
+         * @deprecated per-point compression property will be removed, refer {@link #getEncoded(boolean)}
          */
         public F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, boolean withCompression)
         {
@@ -360,71 +1141,91 @@
             {
                 throw new IllegalArgumentException("Exactly one of the field elements is null");
             }
-            
+
             if (x != null)
             {
                 // Check if x and y are elements of the same field
                 ECFieldElement.F2m.checkFieldElements(this.x, this.y);
-    
+
                 // Check if x and a are elements of the same field
                 if (curve != null)
                 {
                     ECFieldElement.F2m.checkFieldElements(this.x, this.curve.getA());
                 }
             }
-            
+
             this.withCompression = withCompression;
+
+//            checkCurveEquation();
         }
 
-        /* (non-Javadoc)
-         * @see org.bouncycastle.math.ec.ECPoint#getEncoded()
-         */
-        public byte[] getEncoded(boolean compressed)
+        F2m(ECCurve curve, ECFieldElement x, ECFieldElement y, ECFieldElement[] zs, boolean withCompression)
         {
-            if (this.isInfinity()) 
+            super(curve, x, y, zs);
+
+            this.withCompression = withCompression;
+
+//            checkCurveEquation();
+        }
+
+        public ECFieldElement getYCoord()
+        {
+            int coord = this.getCurveCoordinateSystem();
+
+            switch (coord)
             {
-                return new byte[1];
-            }
-
-            int byteCount = converter.getByteLength(this.x);
-            byte[] X = converter.integerToBytes(this.getX().toBigInteger(), byteCount);
-            byte[] PO;
-
-            if (compressed)
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
             {
-                // See X9.62 4.3.6 and 4.2.2
-                PO = new byte[byteCount + 1];
-
-                PO[0] = 0x02;
-                // X9.62 4.2.2 and 4.3.6:
-                // if x = 0 then ypTilde := 0, else ypTilde is the rightmost
-                // bit of y * x^(-1)
-                // if ypTilde = 0, then PC := 02, else PC := 03
-                // Note: PC === PO[0]
-                if (!(this.getX().toBigInteger().equals(ECConstants.ZERO)))
+                // TODO The X == 0 stuff needs further thought
+                if (this.isInfinity() || x.isZero())
                 {
-                    if (this.getY().multiply(this.getX().invert())
-                            .toBigInteger().testBit(0))
-                    {
-                        // ypTilde = 1, hence PC = 03
-                        PO[0] = 0x03;
-                    }
+                    return y;
                 }
 
-                System.arraycopy(X, 0, PO, 1, byteCount);
+                // Y is actually Lambda (X + Y/X) here; convert to affine value on the fly
+                ECFieldElement X = x, L = y;
+                ECFieldElement Y = L.subtract(X).multiply(X);
+                if (ECCurve.COORD_LAMBDA_PROJECTIVE == coord)
+                {
+                    ECFieldElement Z = zs[0];
+                    if (Z.bitLength() != 1)
+                    {
+                        Y = Y.divide(Z);
+                    }
+                }
+                return Y;
             }
-            else
+            default:
             {
-                byte[] Y = converter.integerToBytes(this.getY().toBigInteger(), byteCount);
-    
-                PO = new byte[byteCount + byteCount + 1];
-    
-                PO[0] = 0x04;
-                System.arraycopy(X, 0, PO, 1, byteCount);
-                System.arraycopy(Y, 0, PO, byteCount + 1, byteCount);    
+                return y;
+            }
+            }
+        }
+
+        protected boolean getCompressionYTilde()
+        {
+            ECFieldElement X = this.getRawXCoord();
+            if (X.isZero())
+            {
+                return false;
             }
 
-            return PO;
+            ECFieldElement Y = this.getRawYCoord();
+
+            switch (this.getCurveCoordinateSystem())
+            {
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                // Y is actually Lambda (X + Y/X) here
+                return Y.subtract(X).testBitZero();
+            }
+            default:
+            {
+                return Y.divide(X).testBitZero();
+            }
+            }
         }
 
         /**
@@ -437,7 +1238,7 @@
         private static void checkPoints(ECPoint a, ECPoint b)
         {
             // Check, if points are on the same curve
-            if (!(a.curve.equals(b.curve)))
+            if (a.curve != b.curve)
             {
                 throw new IllegalArgumentException("Only points on the same "
                         + "curve can be added or subtracted");
@@ -466,43 +1267,162 @@
          */
         public ECPoint.F2m addSimple(ECPoint.F2m b)
         {
-            ECPoint.F2m other = b;
             if (this.isInfinity())
             {
-                return other;
+                return b;
             }
-
-            if (other.isInfinity())
+            if (b.isInfinity())
             {
                 return this;
             }
 
-            ECFieldElement.F2m x2 = (ECFieldElement.F2m)other.getX();
-            ECFieldElement.F2m y2 = (ECFieldElement.F2m)other.getY();
+            ECCurve curve = this.getCurve();
+            int coord = curve.getCoordinateSystem();
 
-            // Check if other = this or other = -this
-            if (this.x.equals(x2))
+            ECFieldElement X1 = this.x;
+            ECFieldElement X2 = b.x;
+
+            switch (coord)
             {
-                if (this.y.equals(y2))
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement Y1 = this.y;
+                ECFieldElement Y2 = b.y;
+
+                if (X1.equals(X2))
                 {
-                    // this = other, i.e. this must be doubled
-                    return (ECPoint.F2m)this.twice();
+                    if (Y1.equals(Y2))
+                    {
+                        return (ECPoint.F2m)twice();
+                    }
+
+                    return (ECPoint.F2m)curve.getInfinity();
                 }
 
-                // this = -other, i.e. the result is the point at infinity
-                return (ECPoint.F2m)this.curve.getInfinity();
+                ECFieldElement sumX = X1.add(X2);
+                ECFieldElement L = Y1.add(Y2).divide(sumX);
+
+                ECFieldElement X3 = L.square().add(L).add(sumX).add(curve.getA());
+                ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+
+                return new ECPoint.F2m(curve, X3, Y3, this.withCompression);
             }
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+                ECFieldElement Y2 = b.y, Z2 = b.zs[0];
 
-            ECFieldElement.F2m lambda
-                = (ECFieldElement.F2m)(this.y.add(y2)).divide(this.x.add(x2));
+                boolean Z2IsOne = Z2.bitLength() == 1;
 
-            ECFieldElement.F2m x3
-                = (ECFieldElement.F2m)lambda.square().add(lambda).add(this.x).add(x2).add(this.curve.getA());
+                ECFieldElement U1 = Z1.multiply(Y2); 
+                ECFieldElement U2 = Z2IsOne ? Y1 : Y1.multiply(Z2);
+                ECFieldElement U = U1.subtract(U2);
+                ECFieldElement V1 = Z1.multiply(X2);
+                ECFieldElement V2 = Z2IsOne ? X1 : X1.multiply(Z2);
+                ECFieldElement V = V1.subtract(V2);
 
-            ECFieldElement.F2m y3
-                = (ECFieldElement.F2m)lambda.multiply(this.x.add(x3)).add(x3).add(this.y);
+                if (V1.equals(V2))
+                {
+                    if (U1.equals(U2))
+                    {
+                        return (ECPoint.F2m)twice();
+                    }
 
-            return new ECPoint.F2m(curve, x3, y3, withCompression);
+                    return (ECPoint.F2m)curve.getInfinity();
+                }
+
+                ECFieldElement VSq =  V.square();
+                ECFieldElement W = Z2IsOne ? Z1 : Z1.multiply(Z2);
+                ECFieldElement A = U.square().add(U.multiply(V).add(VSq.multiply(curve.getA()))).multiply(W).add(V.multiply(VSq));
+
+                ECFieldElement X3 = V.multiply(A);
+                ECFieldElement VSqZ2 = Z2IsOne ? VSq : VSq.multiply(Z2);
+                ECFieldElement Y3 = VSqZ2.multiply(U.multiply(X1).add(Y1.multiply(V))).add(A.multiply(U.add(V)));
+                ECFieldElement Z3 = VSq.multiply(V).multiply(W);
+
+                return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                if (X1.isZero())
+                {
+                    return b.addSimple(this);
+                }
+
+                ECFieldElement L1 = this.y, Z1 = this.zs[0];
+                ECFieldElement L2 = b.y, Z2 = b.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+                ECFieldElement U2 = X2, S2 = L2;
+                if (!Z1IsOne)
+                {
+                    U2 = U2.multiply(Z1);
+                    S2 = S2.multiply(Z1);
+                }
+
+                boolean Z2IsOne = Z2.bitLength() == 1;
+                ECFieldElement U1 = X1, S1 = L1;
+                if (!Z2IsOne)
+                {
+                    U1 = U1.multiply(Z2);
+                    S1 = S1.multiply(Z2);
+                }
+
+                ECFieldElement A = S1.add(S2);
+                ECFieldElement B = U1.add(U2);
+
+                if (B.isZero())
+                {
+                    if (A.isZero())
+                    {
+                        return (ECPoint.F2m)twice();
+                    }
+
+                    return (ECPoint.F2m)curve.getInfinity();
+                }
+
+                ECFieldElement X3, L3, Z3;
+                if (X2.isZero())
+                {
+                    // TODO This can probably be optimized quite a bit
+
+                    ECFieldElement Y1 = getYCoord(), Y2 = L2;
+                    ECFieldElement L = Y1.add(Y2).divide(X1);
+
+                    X3 = L.square().add(L).add(X1).add(curve.getA());
+                    ECFieldElement Y3 = L.multiply(X1.add(X3)).add(X3).add(Y1);
+                    L3 = X3.isZero() ? Y3 : Y3.divide(X3).add(X3);
+                    Z3 = curve.fromBigInteger(ECConstants.ONE);
+                }
+                else
+                {
+                    B = B.square();
+    
+                    ECFieldElement AU1 = A.multiply(U1);
+                    ECFieldElement AU2 = A.multiply(U2);
+                    ECFieldElement ABZ2 = A.multiply(B);
+                    if (!Z2IsOne)
+                    {
+                        ABZ2 = ABZ2.multiply(Z2);
+                    }
+
+                    X3 = AU1.multiply(AU2);
+                    L3 = AU2.add(B).square().add(ABZ2.multiply(L1.add(Z1)));
+
+                    Z3 = ABZ2;
+                    if (!Z1IsOne)
+                    {
+                        Z3 = Z3.multiply(Z1);
+                    }
+                }
+
+                return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+            default:
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
+            }
         }
 
         /* (non-Javadoc)
@@ -534,59 +1454,279 @@
             return addSimple((ECPoint.F2m)b.negate());
         }
 
-        /* (non-Javadoc)
-         * @see org.bouncycastle.math.ec.ECPoint#twice()
-         */
+        public ECPoint.F2m tau()
+        {
+            if (this.isInfinity())
+            {
+                return this;
+            }
+
+            ECCurve curve = this.getCurve();
+            int coord = curve.getCoordinateSystem();
+
+            ECFieldElement X1 = this.x;
+
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            {
+                ECFieldElement Y1 = this.y;
+                return new ECPoint.F2m(curve, X1.square(), Y1.square(), this.withCompression);
+            }
+            case ECCurve.COORD_HOMOGENEOUS:
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+                return new ECPoint.F2m(curve, X1.square(), Y1.square(), new ECFieldElement[]{ Z1.square() }, this.withCompression);
+            }
+            default:
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
+            }
+        }
+
         public ECPoint twice()
         {
             if (this.isInfinity()) 
             {
-                // Twice identity element (point at infinity) is identity
                 return this;
             }
 
-            if (this.x.toBigInteger().signum() == 0) 
+            ECCurve curve = this.getCurve();
+
+            ECFieldElement X1 = this.x;
+            if (X1.isZero()) 
             {
-                // if x1 == 0, then (x1, y1) == (x1, x1 + y1)
-                // and hence this = -this and thus 2(x1, y1) == infinity
-                return this.curve.getInfinity();
+                // A point with X == 0 is it's own additive inverse
+                return curve.getInfinity();
             }
 
-            ECFieldElement.F2m lambda
-                = (ECFieldElement.F2m)this.x.add(this.y.divide(this.x));
+            int coord = curve.getCoordinateSystem();
 
-            ECFieldElement.F2m x3
-                = (ECFieldElement.F2m)lambda.square().add(lambda).
-                    add(this.curve.getA());
+            switch (coord)
+            {
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement Y1 = this.y;
 
-            ECFieldElement ONE = this.curve.fromBigInteger(ECConstants.ONE);
-            ECFieldElement.F2m y3
-                = (ECFieldElement.F2m)this.x.square().add(
-                    x3.multiply(lambda.add(ONE)));
+                ECFieldElement L1 = Y1.divide(X1).add(X1);
 
-            return new ECPoint.F2m(this.curve, x3, y3, withCompression);
+                ECFieldElement X3 = L1.square().add(L1).add(curve.getA());
+                ECFieldElement Y3 = X1.square().add(X3.multiply(L1.addOne()));
+
+                return new ECPoint.F2m(curve, X3, Y3, this.withCompression);
+            }
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Y1 = this.y, Z1 = this.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+                ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+                ECFieldElement Y1Z1 = Z1IsOne ? Y1 : Y1.multiply(Z1);
+
+                ECFieldElement X1Sq = X1.square();
+                ECFieldElement S = X1Sq.add(Y1Z1);
+                ECFieldElement V = X1Z1;
+                ECFieldElement vSquared = V.square();
+                ECFieldElement h = S.square().add(S.multiply(V)).add(curve.getA().multiply(vSquared));
+
+                ECFieldElement X3 = V.multiply(h);
+                ECFieldElement Y3 = h.multiply(S.add(V)).add(X1Sq.square().multiply(V));
+                ECFieldElement Z3 = V.multiply(vSquared);    
+
+                return new ECPoint.F2m(curve, X3, Y3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                ECFieldElement L1 = this.y, Z1 = this.zs[0];
+
+                boolean Z1IsOne = Z1.bitLength() == 1;
+                ECFieldElement L1Z1 = Z1IsOne ? L1 : L1.multiply(Z1);
+                ECFieldElement Z1Sq = Z1IsOne ? Z1 : Z1.square();
+                ECFieldElement a = curve.getA();
+                ECFieldElement aZ1Sq = Z1IsOne ? a : a.multiply(Z1Sq);
+                ECFieldElement T = L1.square().add(L1Z1).add(aZ1Sq);
+
+                ECFieldElement X3 = T.square();
+                ECFieldElement Z3 = Z1IsOne ? T : T.multiply(Z1Sq);
+
+                ECFieldElement b = curve.getB();
+                ECFieldElement L3;
+                if (b.bitLength() < (curve.getFieldSize() >> 1))
+                {
+                    ECFieldElement t1 = L1.add(X1).square();
+                    ECFieldElement t2 = aZ1Sq.square();
+                    ECFieldElement t3 = curve.getB().multiply(Z1Sq.square());
+                    L3 = t1.add(T).add(Z1Sq).multiply(t1).add(t2.add(t3)).add(X3).add(a.addOne().multiply(Z3));
+                }
+                else
+                {
+                    ECFieldElement X1Z1 = Z1IsOne ? X1 : X1.multiply(Z1);
+                    L3 = X1Z1.square().add(X3).add(T.multiply(L1Z1)).add(Z3);
+                }
+
+                return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+            default:
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
+            }
+        }
+
+        public ECPoint twicePlus(ECPoint b)
+        {
+            if (this.isInfinity()) 
+            {
+                return b;
+            }
+            if (b.isInfinity())
+            {
+                return twice();
+            }
+
+            ECCurve curve = this.getCurve();
+
+            ECFieldElement X1 = this.x;
+            if (X1.isZero()) 
+            {
+                // A point with X == 0 is it's own additive inverse
+                return b;
+            }
+
+            int coord = curve.getCoordinateSystem();
+
+            switch (coord)
+            {
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                // NOTE: twicePlus() only optimized for lambda-affine argument
+                ECFieldElement X2 = b.x, Z2 = b.zs[0];
+                if (X2.isZero() || Z2.bitLength() != 1)
+                {
+                    return twice().add(b);
+                }
+
+                ECFieldElement L1 = this.y, Z1 = this.zs[0];
+                ECFieldElement L2 = b.y;
+
+                ECFieldElement X1Sq = X1.square();
+                ECFieldElement L1Sq = L1.square();
+                ECFieldElement Z1Sq = Z1.square();
+                ECFieldElement L1Z1 = L1.multiply(Z1);
+
+                ECFieldElement T = curve.getA().multiply(Z1Sq).add(L1Sq).add(L1Z1);
+                ECFieldElement L2plus1 = L2.addOne();
+                ECFieldElement A = curve.getA().add(L2plus1).multiply(Z1Sq).add(L1Sq).multiply(T).add(X1Sq.multiply(Z1Sq));
+                ECFieldElement X2Z1Sq = X2.multiply(Z1Sq);
+                ECFieldElement B = X2Z1Sq.add(T).square();
+
+                ECFieldElement X3 = A.square().multiply(X2Z1Sq);
+                ECFieldElement Z3 = A.multiply(B).multiply(Z1Sq);
+                ECFieldElement L3 = A.add(B).square().multiply(T).add(L2plus1.multiply(Z3));
+
+                return new ECPoint.F2m(curve, X3, L3, new ECFieldElement[]{ Z3 }, this.withCompression);
+            }
+            default:
+            {
+                return twice().add(b);
+            }
+            }
+        }
+
+        protected void checkCurveEquation()
+        {
+            if (this.isInfinity())
+            {
+                return;
+            }
+
+            ECFieldElement Z;
+            switch (this.getCurveCoordinateSystem())
+            {
+            case ECCurve.COORD_LAMBDA_AFFINE:
+                Z = curve.fromBigInteger(ECConstants.ONE);
+                break;
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+                Z = this.zs[0];
+                break;
+            default:
+                return;
+            }
+
+            if (Z.isZero())
+            {
+                throw new IllegalStateException();
+            }
+
+            ECFieldElement X = this.x;
+            if (X.isZero())
+            {
+                // NOTE: For x == 0, we expect the affine-y instead of the lambda-y 
+                ECFieldElement Y = this.y;
+                if (!Y.square().equals(curve.getB().multiply(Z)))
+                {
+                    throw new IllegalStateException();
+                }
+
+                return;
+            }
+
+            ECFieldElement L = this.y;
+            ECFieldElement XSq = X.square();
+            ECFieldElement ZSq = Z.square();
+
+            ECFieldElement lhs = L.square().add(L.multiply(Z)).add(this.getCurve().getA().multiply(ZSq)).multiply(XSq);
+            ECFieldElement rhs = ZSq.square().multiply(this.getCurve().getB()).add(XSq.square());
+            
+            if (!lhs.equals(rhs))
+            {
+                throw new IllegalStateException("F2m Lambda-Projective invariant broken");
+            }
         }
 
         public ECPoint negate()
         {
-            return new ECPoint.F2m(curve, this.getX(), this.getY().add(this.getX()), withCompression);
-        }
-
-        /**
-         * Sets the appropriate <code>ECMultiplier</code>, unless already set. 
-         */
-        synchronized void assertECMultiplier()
-        {
-            if (this.multiplier == null)
+            if (this.isInfinity())
             {
-                if (((ECCurve.F2m)this.curve).isKoblitz())
-                {
-                    this.multiplier = new WTauNafMultiplier();
-                }
-                else
-                {
-                    this.multiplier = new WNafMultiplier();
-                }
+                return this;
+            }
+
+            ECFieldElement X = this.x;
+            if (X.isZero())
+            {
+                return this;
+            }
+
+            switch (this.getCurveCoordinateSystem())
+            {
+            case ECCurve.COORD_AFFINE:
+            {
+                ECFieldElement Y = this.y;
+                return new ECPoint.F2m(curve, X, Y.add(X), this.withCompression);
+            }
+            case ECCurve.COORD_HOMOGENEOUS:
+            {
+                ECFieldElement Y = this.y, Z = this.zs[0];
+                return new ECPoint.F2m(curve, X, Y.add(X), new ECFieldElement[]{ Z }, this.withCompression);
+            }
+            case ECCurve.COORD_LAMBDA_AFFINE:
+            {
+                ECFieldElement L = this.y;
+                return new ECPoint.F2m(curve, X, L.addOne(), this.withCompression);
+            }
+            case ECCurve.COORD_LAMBDA_PROJECTIVE:
+            {
+                // L is actually Lambda (X + Y/X) here
+                ECFieldElement L = this.y, Z = this.zs[0];
+                return new ECPoint.F2m(curve, X, L.add(Z), new ECFieldElement[]{ Z }, this.withCompression);
+            }
+            default:
+            {
+                throw new IllegalStateException("unsupported coordinate system");
+            }
             }
         }
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java
deleted file mode 100644
index 35e601d..0000000
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/FpNafMultiplier.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package org.bouncycastle.math.ec;
-
-import java.math.BigInteger;
-
-/**
- * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm.
- */
-class FpNafMultiplier implements ECMultiplier
-{
-    /**
-     * D.3.2 pg 101
-     * @see org.bouncycastle.math.ec.ECMultiplier#multiply(org.bouncycastle.math.ec.ECPoint, java.math.BigInteger)
-     */
-    public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
-    {
-        // TODO Probably should try to add this
-        // BigInteger e = k.mod(n); // n == order of p
-        BigInteger e = k;
-        BigInteger h = e.multiply(BigInteger.valueOf(3));
-
-        ECPoint neg = p.negate();
-        ECPoint R = p;
-
-        for (int i = h.bitLength() - 2; i > 0; --i)
-        {             
-            R = R.twice();
-
-            boolean hBit = h.testBit(i);
-            boolean eBit = e.testBit(i);
-
-            if (hBit != eBit)
-            {
-                R = R.add(hBit ? p : neg);
-            }
-        }
-
-        return R;
-    }
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java
index ead38c4..34395a5 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/IntArray.java
@@ -6,6 +6,60 @@
 
 class IntArray
 {
+//    private static int DEINTERLEAVE_MASK = 0x55555555;
+
+    /*
+     * This expands 8 bit indices into 16 bit contents, by inserting 0s between bits.
+     * In a binary field, this operation is the same as squaring an 8 bit number.
+     */
+    private static final int[] INTERLEAVE_TABLE = new int[] { 0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014,
+        0x0015, 0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055, 0x0100, 0x0101, 0x0104, 0x0105, 0x0110,
+        0x0111, 0x0114, 0x0115, 0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155, 0x0400, 0x0401, 0x0404,
+        0x0405, 0x0410, 0x0411, 0x0414, 0x0415, 0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455, 0x0500,
+        0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515, 0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554,
+        0x0555, 0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015, 0x1040, 0x1041, 0x1044, 0x1045, 0x1050,
+        0x1051, 0x1054, 0x1055, 0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115, 0x1140, 0x1141, 0x1144,
+        0x1145, 0x1150, 0x1151, 0x1154, 0x1155, 0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415, 0x1440,
+        0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455, 0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514,
+        0x1515, 0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555, 0x4000, 0x4001, 0x4004, 0x4005, 0x4010,
+        0x4011, 0x4014, 0x4015, 0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055, 0x4100, 0x4101, 0x4104,
+        0x4105, 0x4110, 0x4111, 0x4114, 0x4115, 0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155, 0x4400,
+        0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415, 0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454,
+        0x4455, 0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515, 0x4540, 0x4541, 0x4544, 0x4545, 0x4550,
+        0x4551, 0x4554, 0x4555, 0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015, 0x5040, 0x5041, 0x5044,
+        0x5045, 0x5050, 0x5051, 0x5054, 0x5055, 0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115, 0x5140,
+        0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155, 0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414,
+        0x5415, 0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455, 0x5500, 0x5501, 0x5504, 0x5505, 0x5510,
+        0x5511, 0x5514, 0x5515, 0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555 };
+
+    // For toString(); must have length 32
+    private static final String ZEROES = "00000000000000000000000000000000";
+
+    private final static byte[] bitLengths =
+    {
+        0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
+    };
+
+    public static int getWordLength(int bits)
+    {
+        return (bits + 31) >>> 5;
+    }
+
     // TODO make m fixed for the IntArray, and hence compute T once and for all
 
     private int[] m_ints;
@@ -22,16 +76,12 @@
 
     public IntArray(BigInteger bigInt)
     {
-        this(bigInt, 0);
-    }
-
-    public IntArray(BigInteger bigInt, int minIntLen)
-    {
-        if (bigInt.signum() == -1)
+        if (bigInt == null || bigInt.signum() < 0)
         {
-            throw new IllegalArgumentException("Only positive Integers allowed");
+            throw new IllegalArgumentException("invalid F2m field value");
         }
-        if (bigInt.equals(ECConstants.ZERO))
+
+        if (bigInt.signum() == 0)
         {
             m_ints = new int[] { 0 };
             return;
@@ -48,14 +98,7 @@
             barrStart = 1;
         }
         int intLen = (barrLen + 3) / 4;
-        if (intLen < minIntLen)
-        {
-            m_ints = new int[minIntLen];
-        }
-        else
-        {
-            m_ints = new int[intLen];
-        }
+        m_ints = new int[intLen];
 
         int iarrJ = intLen - 1;
         int rem = barrLen % 4 + barrStart;
@@ -66,11 +109,7 @@
             for (; barrI < rem; barrI++)
             {
                 temp <<= 8;
-                int barrBarrI = barr[barrI];
-                if (barrBarrI < 0)
-                {
-                    barrBarrI += 256;
-                }
+                int barrBarrI = barr[barrI] & 0xFF;
                 temp |= barrBarrI;
             }
             m_ints[iarrJ--] = temp;
@@ -82,11 +121,7 @@
             for (int i = 0; i < 4; i++)
             {
                 temp <<= 8;
-                int barrBarrI = barr[barrI++];
-                if (barrBarrI < 0)
-                {
-                    barrBarrI += 256;
-                }
+                int barrBarrI = barr[barrI++] & 0xFF;
                 temp |= barrBarrI;
             }
             m_ints[iarrJ] = temp;
@@ -95,88 +130,86 @@
 
     public boolean isZero()
     {
-        return m_ints.length == 0
-            || (m_ints[0] == 0 && getUsedLength() == 0);
+        int[] a = m_ints;
+        for (int i = 0; i < a.length; ++i)
+        {
+            if (a[i] != 0)
+            {
+                return false;
+            }
+        }
+        return true;
     }
 
     public int getUsedLength()
     {
-        int highestIntPos = m_ints.length;
+        return getUsedLengthFrom(m_ints.length);
+    }
 
-        if (highestIntPos < 1)
+    public int getUsedLengthFrom(int from)
+    {
+        int[] a = m_ints;
+        from = Math.min(from, a.length);
+
+        if (from < 1)
         {
             return 0;
         }
 
         // Check if first element will act as sentinel
-        if (m_ints[0] != 0)
+        if (a[0] != 0)
         {
-            while (m_ints[--highestIntPos] == 0)
+            while (a[--from] == 0)
             {
             }
-            return highestIntPos + 1;
+            return from + 1;
         }
 
         do
         {
-            if (m_ints[--highestIntPos] != 0)
+            if (a[--from] != 0)
             {
-                return highestIntPos + 1;
+                return from + 1;
             }
         }
-        while (highestIntPos > 0);
+        while (from > 0);
 
         return 0;
     }
 
-    public int bitLength()
+    public int degree()
     {
-        // JDK 1.5: see Integer.numberOfLeadingZeros()
-        int intLen = getUsedLength();
-        if (intLen == 0)
+        int i = m_ints.length, w;
+        do
         {
-            return 0;
-        }
-
-        int last = intLen - 1;
-        int highest = m_ints[last];
-        int bits = (last << 5) + 1;
-
-        // A couple of binary search steps
-        if ((highest & 0xffff0000) != 0)
-        {
-            if ((highest & 0xff000000) != 0)
+            if (i == 0)
             {
-                bits += 24;
-                highest >>>= 24;
+                return 0;
             }
-            else
-            {
-                bits += 16;
-                highest >>>= 16;
-            }
+            w = m_ints[--i];
         }
-        else if (highest > 0x000000ff)
+        while (w == 0);
+
+        return (i << 5) + bitLength(w);
+    }
+
+    private static int bitLength(int w)
+    {
+        int t = w >>> 16;
+        if (t == 0)
         {
-            bits += 8;
-            highest >>>= 8;
+            t = w >>> 8;
+            return (t == 0) ? bitLengths[w] : 8 + bitLengths[t];
         }
 
-        while (highest != 1)
-        {
-            ++bits;
-            highest >>>= 1;
-        }
-
-        return bits;
+        int u = t >>> 8;
+        return (u == 0) ? 16 + bitLengths[t] : 24 + bitLengths[u];
     }
 
     private int[] resizedInts(int newLen)
     {
         int[] newInts = new int[newLen];
-        int oldLen = m_ints.length;
-        int copyLen = oldLen < newLen ? oldLen : newLen;
-        System.arraycopy(m_ints, 0, newInts, 0, copyLen);
+        System.arraycopy(m_ints, 0, newInts, 0, Math.min(m_ints.length, newLen));
         return newInts;
     }
 
@@ -220,86 +253,128 @@
         return new BigInteger(1, barr);
     }
 
-    public void shiftLeft()
+    private static int shiftLeft(int[] x, int count)
     {
-        int usedLen = getUsedLength();
-        if (usedLen == 0)
+        int prev = 0;
+        for (int i = 0; i < count; ++i)
+        {
+            int next = x[i];
+            x[i] = (next << 1) | prev;
+            prev = next >>> 31;
+        }
+        return prev;
+    }
+
+    public void addOneShifted(int shift)
+    {
+        if (shift >= m_ints.length)
+        {
+            m_ints = resizedInts(shift + 1);
+        }
+
+        m_ints[shift] ^= 1;
+    }
+
+    private void addShiftedByBits(IntArray other, int bits)
+    {
+        int words = bits >>> 5;
+        int shift = bits & 0x1F;
+
+        if (shift == 0)
+        {
+            addShiftedByWords(other, words);
+            return;
+        }
+
+        int otherUsedLen = other.getUsedLength();
+        if (otherUsedLen == 0)
         {
             return;
         }
-        if (m_ints[usedLen - 1] < 0)
+
+        int minLen = otherUsedLen + words + 1;
+        if (minLen > m_ints.length)
         {
-            // highest bit of highest used byte is set, so shifting left will
-            // make the IntArray one byte longer
-            usedLen++;
-            if (usedLen > m_ints.length)
-            {
-                // make the m_ints one byte longer, because we need one more
-                // byte which is not available in m_ints
-                m_ints = resizedInts(m_ints.length + 1);
-            }
+            m_ints = resizedInts(minLen);
         }
 
-        boolean carry = false;
-        for (int i = 0; i < usedLen; i++)
+        int shiftInv = 32 - shift, prev = 0;
+        for (int i = 0; i < otherUsedLen; ++i)
         {
-            // nextCarry is true if highest bit is set
-            boolean nextCarry = m_ints[i] < 0;
-            m_ints[i] <<= 1;
-            if (carry)
-            {
-                // set lowest bit
-                m_ints[i] |= 1;
-            }
-            carry = nextCarry;
+            int next = other.m_ints[i];
+            m_ints[i + words] ^= (next << shift) | prev;
+            prev = next >>> shiftInv;
+        }
+        m_ints[otherUsedLen + words] ^= prev;
+    }
+
+    private static int addShiftedByBits(int[] x, int[] y, int count, int shift)
+    {
+        int shiftInv = 32 - shift, prev = 0;
+        for (int i = 0; i < count; ++i)
+        {
+            int next = y[i];
+            x[i] ^= (next << shift) | prev;
+            prev = next >>> shiftInv;
+        }
+        return prev;
+    }
+
+    private static int addShiftedByBits(int[] x, int xOff, int[] y, int yOff, int count, int shift)
+    {
+        int shiftInv = 32 - shift, prev = 0;
+        for (int i = 0; i < count; ++i)
+        {
+            int next = y[yOff + i];
+            x[xOff + i] ^= (next << shift) | prev;
+            prev = next >>> shiftInv;
+        }
+        return prev;
+    }
+
+    public void addShiftedByWords(IntArray other, int words)
+    {
+        int otherUsedLen = other.getUsedLength();
+        if (otherUsedLen == 0)
+        {
+            return;
+        }
+
+        int minLen = otherUsedLen + words;
+        if (minLen > m_ints.length)
+        {
+            m_ints = resizedInts(minLen);
+        }
+
+        for (int i = 0; i < otherUsedLen; i++)
+        {
+            m_ints[words + i] ^= other.m_ints[i];
         }
     }
 
-    public IntArray shiftLeft(int n)
+    private static void addShiftedByWords(int[] x, int xOff, int[] y, int count)
     {
-        int usedLen = getUsedLength();
-        if (usedLen == 0)
+        for (int i = 0; i < count; ++i)
         {
-            return this;
+            x[xOff + i] ^= y[i];
         }
-
-        if (n == 0)
-        {
-            return this;
-        }
-
-        if (n > 31)
-        {
-            throw new IllegalArgumentException("shiftLeft() for max 31 bits "
-                + ", " + n + "bit shift is not possible");
-        }
-
-        int[] newInts = new int[usedLen + 1];
-
-        int nm32 = 32 - n;
-        newInts[0] = m_ints[0] << n;
-        for (int i = 1; i < usedLen; i++)
-        {
-            newInts[i] = (m_ints[i] << n) | (m_ints[i - 1] >>> nm32);
-        }
-        newInts[usedLen] = m_ints[usedLen - 1] >>> nm32;
-
-        return new IntArray(newInts);
     }
 
-    public void addShifted(IntArray other, int shift)
+    private static void add(int[] x, int[] y, int count)
     {
-        int usedLenOther = other.getUsedLength();
-        int newMinUsedLen = usedLenOther + shift;
-        if (newMinUsedLen > m_ints.length)
+        for (int i = 0; i < count; ++i)
         {
-            m_ints = resizedInts(newMinUsedLen);
-            //System.out.println("Resize required");
+            x[i] ^= y[i];
         }
+    }
 
-        for (int i = 0; i < usedLenOther; i++)
+    private static void distribute(int[] x, int dst1, int dst2, int src, int count)
+    {
+        for (int i = 0; i < count; ++i)
         {
-            m_ints[i + shift] ^= other.m_ints[i];
+            int v = x[src + i];
+            x[dst1 + i] ^= v;
+            x[dst2 + i] ^= v;
         }
     }
 
@@ -308,10 +383,58 @@
         return m_ints.length;
     }
 
+    public void flipWord(int bit, int word)
+    {
+        int len = m_ints.length;
+        int n = bit >>> 5;
+        if (n < len)
+        {
+            int shift = bit & 0x1F;
+            if (shift == 0)
+            {
+                m_ints[n] ^= word;
+            }
+            else
+            {
+                m_ints[n] ^= word << shift;
+                if (++n < len)
+                {
+                    m_ints[n] ^= word >>> (32 - shift);
+                }
+            }
+        }
+    }
+
+    public int getWord(int bit)
+    {
+        int len = m_ints.length;
+        int n = bit >>> 5;
+        if (n >= len)
+        {
+            return 0;
+        }
+        int shift = bit & 0x1F;
+        if (shift == 0)
+        {
+            return m_ints[n];
+        }
+        int result = m_ints[n] >>> shift;
+        if (++n < len)
+        {
+            result |= m_ints[n] << (32 - shift);
+        }
+        return result;
+    }
+
+    public boolean testBitZero()
+    {
+        return m_ints.length > 0 && (m_ints[0] & 1) != 0;
+    }
+
     public boolean testBit(int n)
     {
         // theInt = n / 32
-        int theInt = n >> 5;
+        int theInt = n >>> 5;
         // theBit = n % 32
         int theBit = n & 0x1F;
         int tester = 1 << theBit;
@@ -321,7 +444,7 @@
     public void flipBit(int n)
     {
         // theInt = n / 32
-        int theInt = n >> 5;
+        int theInt = n >>> 5;
         // theBit = n % 32
         int theBit = n & 0x1F;
         int flipper = 1 << theBit;
@@ -331,127 +454,344 @@
     public void setBit(int n)
     {
         // theInt = n / 32
-        int theInt = n >> 5;
+        int theInt = n >>> 5;
         // theBit = n % 32
         int theBit = n & 0x1F;
         int setter = 1 << theBit;
         m_ints[theInt] |= setter;
     }
 
-    public IntArray multiply(IntArray other, int m)
+    public void clearBit(int n)
     {
-        // Lenght of c is 2m bits rounded up to the next int (32 bit)
-        int t = (m + 31) >> 5;
-        if (m_ints.length < t)
-        {
-            m_ints = resizedInts(t);
-        }
-
-        IntArray b = new IntArray(other.resizedInts(other.getLength() + 1));
-        IntArray c = new IntArray((m + m + 31) >> 5);
-        // IntArray c = new IntArray(t + t);
-        int testBit = 1;
-        for (int k = 0; k < 32; k++)
-        {
-            for (int j = 0; j < t; j++)
-            {
-                if ((m_ints[j] & testBit) != 0)
-                {
-                    // The kth bit of m_ints[j] is set
-                    c.addShifted(b, j);
-                }
-            }
-            testBit <<= 1;
-            b.shiftLeft();
-        }
-        return c;
+        // theInt = n / 32
+        int theInt = n >>> 5;
+        // theBit = n % 32
+        int theBit = n & 0x1F;
+        int setter = 1 << theBit;
+        m_ints[theInt] &= ~setter;
     }
 
-    // public IntArray multiplyLeftToRight(IntArray other, int m) {
-    // // Lenght of c is 2m bits rounded up to the next int (32 bit)
-    // int t = (m + 31) / 32;
-    // if (m_ints.length < t) {
-    // m_ints = resizedInts(t);
-    // }
-    //
-    // IntArray b = new IntArray(other.resizedInts(other.getLength() + 1));
-    // IntArray c = new IntArray((m + m + 31) / 32);
-    // // IntArray c = new IntArray(t + t);
-    // int testBit = 1 << 31;
-    // for (int k = 31; k >= 0; k--) {
-    // for (int j = 0; j < t; j++) {
-    // if ((m_ints[j] & testBit) != 0) {
-    // // The kth bit of m_ints[j] is set
-    // c.addShifted(b, j);
-    // }
-    // }
-    // testBit >>>= 1;
-    // if (k > 0) {
-    // c.shiftLeft();
-    // }
-    // }
-    // return c;
-    // }
-
-    // TODO note, redPol.length must be 3 for TPB and 5 for PPB
-    public void reduce(int m, int[] redPol)
+    public IntArray multiply(IntArray other, int m)
     {
-        for (int i = m + m - 2; i >= m; i--)
+        int aLen = getUsedLength();
+        if (aLen == 0)
+        {
+            return new IntArray(1);
+        }
+
+        int bLen = other.getUsedLength();
+        if (bLen == 0)
+        {
+            return new IntArray(1);
+        }
+
+        IntArray A = this, B = other;
+        if (aLen > bLen)
+        {
+            A = other; B = this;
+            int tmp = aLen; aLen = bLen; bLen = tmp;
+        }
+
+        if (aLen == 1)
+        {
+            int a = A.m_ints[0];
+            int[] b = B.m_ints;
+            int[] c = new int[aLen + bLen];
+            if ((a & 1) != 0)
+            {
+                add(c, b, bLen);
+            }
+            int k = 1;
+            while ((a >>>= 1) != 0)
+            {
+                if ((a & 1) != 0)
+                {
+                    addShiftedByBits(c, b, bLen, k);
+                }
+                ++k;
+            }
+            return new IntArray(c);
+        }
+
+        // TODO It'd be better to be able to tune the width directly (need support for interleaving arbitrary widths)
+        int complexity = aLen <= 8 ? 1 : 2;
+
+        int width = 1 << complexity;
+        int shifts = (32 >>> complexity);
+
+        int bExt = bLen;
+        if ((B.m_ints[bLen - 1] >>> (33 - shifts)) != 0)
+        {
+            ++bExt;
+        }
+
+        int cLen = bExt + aLen;
+
+        int[] c = new int[cLen << width];
+        System.arraycopy(B.m_ints, 0, c, 0, bLen);
+        interleave(A.m_ints, 0, c, bExt, aLen, complexity);
+
+        int[] ci = new int[1 << width];
+        for (int i = 1; i < ci.length; ++i)
+        {
+            ci[i] = ci[i - 1] + cLen;
+        }
+
+        int MASK = (1 << width) - 1;
+
+        int k = 0;
+        for (;;)
+        {
+            for (int aPos = 0; aPos < aLen; ++aPos)
+            {
+                int index = (c[bExt + aPos] >>> k) & MASK;
+                if (index != 0)
+                {
+                    addShiftedByWords(c, aPos + ci[index], c, bExt);
+                }
+            }
+
+            if ((k += width) >= 32)
+            {
+                break;
+            }
+
+            shiftLeft(c, bExt);
+        }
+
+        int ciPos = ci.length, pow2 = ciPos >>> 1, offset = 32;
+        while (--ciPos > 1)
+        {
+            if (ciPos == pow2)
+            {
+                offset -= shifts;
+                addShiftedByBits(c, ci[1], c, ci[pow2], cLen, offset);
+                pow2 >>>= 1;
+            }
+            else
+            {
+                distribute(c, ci[pow2], ci[ciPos - pow2], ci[ciPos], cLen);
+            }
+        }
+
+        // TODO reduce in place to avoid extra copying
+        IntArray p = new IntArray(cLen);
+        System.arraycopy(c, ci[1], p.m_ints, 0, cLen);
+        return p;
+    }
+
+//    private static void deInterleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds)
+//    {
+//        for (int i = 0; i < count; ++i)
+//        {
+//            z[zOff + i] = deInterleave(x[zOff + i], rounds);
+//        }
+//    }
+//
+//    private static int deInterleave(int x, int rounds)
+//    {
+//        while (--rounds >= 0)
+//        {
+//            x = deInterleave16(x & DEINTERLEAVE_MASK) | (deInterleave16((x >>> 1) & DEINTERLEAVE_MASK) << 16);
+//        }
+//        return x;
+//    }
+//
+//    private static int deInterleave16(int x)
+//    {
+//        x = (x | (x >>> 1)) & 0x33333333;
+//        x = (x | (x >>> 2)) & 0x0F0F0F0F;
+//        x = (x | (x >>> 4)) & 0x00FF00FF;
+//        x = (x | (x >>> 8)) & 0x0000FFFF;
+//        return x;
+//    }
+
+    public void reduce(int m, int[] ks)
+    {
+        int len = getUsedLength();
+        int mLen = (m + 31) >>> 5;
+        if (len < mLen)
+        {
+            return;
+        }
+
+        int _2m = m << 1;
+        int pos = Math.min(_2m - 2, (len << 5) - 1);
+
+        int kMax = ks[ks.length - 1];
+        if (kMax < m - 31)
+        {
+            reduceWordWise(pos, m, ks);
+        }
+        else
+        {
+            reduceBitWise(pos, m, ks);
+        }
+
+        // Instead of flipping the high bits in the loop, explicitly clear any partial word above m bits
+        int partial = m & 0x1F;
+        if (partial != 0)
+        {
+            m_ints[mLen - 1] &= (1 << partial) - 1;
+        }
+
+        if (len > mLen)
+        {
+            m_ints = resizedInts(mLen);
+        }
+    }
+
+    private void reduceBitWise(int from, int m, int[] ks)
+    {
+        for (int i = from; i >= m; --i)
         {
             if (testBit(i))
             {
+//                clearBit(i);
                 int bit = i - m;
                 flipBit(bit);
-                flipBit(i);
-                int l = redPol.length;
-                while (--l >= 0)
+                int j = ks.length;
+                while (--j >= 0)
                 {
-                    flipBit(redPol[l] + bit);
+                    flipBit(ks[j] + bit);
                 }
             }
         }
-        m_ints = resizedInts((m + 31) >> 5);
+    }
+
+    private void reduceWordWise(int from, int m, int[] ks)
+    {
+        int pos = m + ((from - m) & ~0x1F);
+        for (int i = pos; i >= m; i -= 32)
+        {
+            int word = getWord(i);
+            if (word != 0)
+            {
+//                flipWord(i);
+                int bit = i - m;
+                flipWord(bit, word);
+                int j = ks.length;
+                while (--j >= 0)
+                {
+                    flipWord(ks[j] + bit, word);
+                }
+            }
+        }
     }
 
     public IntArray square(int m)
     {
-        // TODO make the table static final
-        final int[] table = { 0x0, 0x1, 0x4, 0x5, 0x10, 0x11, 0x14, 0x15, 0x40,
-            0x41, 0x44, 0x45, 0x50, 0x51, 0x54, 0x55 };
-
-        int t = (m + 31) >> 5;
-        if (m_ints.length < t)
+        int len = getUsedLength();
+        if (len == 0)
         {
-            m_ints = resizedInts(t);
+            return this;
         }
 
-        IntArray c = new IntArray(t + t);
+        int _2len = len << 1;
+        int[] r = new int[_2len];
 
-        // TODO twice the same code, put in separate private method
-        for (int i = 0; i < t; i++)
+        int pos = 0;
+        while (pos < _2len)
         {
-            int v0 = 0;
-            for (int j = 0; j < 4; j++)
-            {
-                v0 = v0 >>> 8;
-                int u = (m_ints[i] >>> (j * 4)) & 0xF;
-                int w = table[u] << 24;
-                v0 |= w;
-            }
-            c.m_ints[i + i] = v0;
-
-            v0 = 0;
-            int upper = m_ints[i] >>> 16;
-            for (int j = 0; j < 4; j++)
-            {
-                v0 = v0 >>> 8;
-                int u = (upper >>> (j * 4)) & 0xF;
-                int w = table[u] << 24;
-                v0 |= w;
-            }
-            c.m_ints[i + i + 1] = v0;
+            int mi = m_ints[pos >>> 1];
+            r[pos++] = interleave16(mi & 0xFFFF);
+            r[pos++] = interleave16(mi >>> 16);
         }
-        return c;
+
+        return new IntArray(r);
+    }
+
+    private static void interleave(int[] x, int xOff, int[] z, int zOff, int count, int rounds)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            z[zOff + i] = interleave(x[xOff + i], rounds);
+        }
+    }
+
+    private static int interleave(int x, int rounds)
+    {
+        while (--rounds >= 0)
+        {
+            x = interleave16(x & 0xFFFF) | (interleave16(x >>> 16) << 1);
+        }
+        return x;
+    }
+
+    private static int interleave16(int n)
+    {
+        return INTERLEAVE_TABLE[n & 0xFF] | INTERLEAVE_TABLE[n >>> 8] << 16;
+    }
+
+    public IntArray modInverse(int m, int[] ks)
+    {
+        // Inversion in F2m using the extended Euclidean algorithm
+        // Input: A nonzero polynomial a(z) of degree at most m-1
+        // Output: a(z)^(-1) mod f(z)
+
+        int uzDegree = degree();
+        if (uzDegree == 1)
+        {
+            return this;
+        }
+
+        // u(z) := a(z)
+        IntArray uz = (IntArray)clone();
+
+        int t = getWordLength(m);
+
+        // v(z) := f(z)
+        IntArray vz = new IntArray(t);
+        vz.setBit(m);
+        vz.setBit(0);
+        vz.setBit(ks[0]);
+        if (ks.length > 1) 
+        {
+            vz.setBit(ks[1]);
+            vz.setBit(ks[2]);
+        }
+
+        // g1(z) := 1, g2(z) := 0
+        IntArray g1z = new IntArray(t);
+        g1z.setBit(0);
+        IntArray g2z = new IntArray(t);
+
+        while (uzDegree != 0)
+        {
+            // j := deg(u(z)) - deg(v(z))
+            int j = uzDegree - vz.degree();
+
+            // If j < 0 then: u(z) <-> v(z), g1(z) <-> g2(z), j := -j
+            if (j < 0) 
+            {
+                final IntArray uzCopy = uz;
+                uz = vz;
+                vz = uzCopy;
+
+                final IntArray g1zCopy = g1z;
+                g1z = g2z;
+                g2z = g1zCopy;
+
+                j = -j;
+            }
+
+            // u(z) := u(z) + z^j * v(z)
+            // Note, that no reduction modulo f(z) is required, because
+            // deg(u(z) + z^j * v(z)) <= max(deg(u(z)), j + deg(v(z)))
+            // = max(deg(u(z)), deg(u(z)) - deg(v(z)) + deg(v(z))
+            // = deg(u(z))
+            // uz = uz.xor(vz.shiftLeft(j));
+            uz.addShiftedByBits(vz, j);
+            uzDegree = uz.degree();
+
+            // g1(z) := g1(z) + z^j * g2(z)
+//            g1z = g1z.xor(g2z.shiftLeft(j));
+            if (uzDegree != 0)
+            {
+                g1z.addShiftedByBits(g2z, j);
+            }
+        }
+        return g2z;
     }
 
     public boolean equals(Object o)
@@ -482,7 +822,8 @@
         int hash = 1;
         for (int i = 0; i < usedLen; i++)
         {
-            hash = hash * 31 + m_ints[i];
+            hash *= 31;
+            hash ^= m_ints[i];
         }
         return hash;
     }
@@ -494,25 +835,26 @@
 
     public String toString()
     {
-        int usedLen = getUsedLength();
-        if (usedLen == 0)
+        int i = getUsedLength();
+        if (i == 0)
         {
             return "0";
         }
 
-        StringBuffer sb = new StringBuffer(Integer
-            .toBinaryString(m_ints[usedLen - 1]));
-        for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+        StringBuffer sb = new StringBuffer(Integer.toBinaryString(m_ints[--i]));
+        while (--i >= 0)
         {
-            String hexString = Integer.toBinaryString(m_ints[iarrJ]);
+            String s = Integer.toBinaryString(m_ints[i]);
 
-            // Add leading zeroes, except for highest significant int
-            for (int i = hexString.length(); i < 8; i++)
+            // Add leading zeroes, except for highest significant word
+            int len = s.length();
+            if (len < 32)
             {
-                hexString = "0" + hexString;
+                sb.append(ZEROES.substring(len));
             }
-            sb.append(hexString);
+
+            sb.append(s);
         }
         return sb.toString();
     }
-}
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java
new file mode 100644
index 0000000..7e8b172
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/LongArray.java
@@ -0,0 +1,1995 @@
+package org.bouncycastle.math.ec;
+
+import org.bouncycastle.util.Arrays;
+
+import java.math.BigInteger;
+
+class LongArray
+{
+//    private static long DEINTERLEAVE_MASK = 0x5555555555555555L;
+
+    /*
+     * This expands 8 bit indices into 16 bit contents (high bit 14), by inserting 0s between bits.
+     * In a binary field, this operation is the same as squaring an 8 bit number.
+     */
+    private static final int[] INTERLEAVE2_TABLE = new int[]
+    {
+        0x0000, 0x0001, 0x0004, 0x0005, 0x0010, 0x0011, 0x0014, 0x0015,
+        0x0040, 0x0041, 0x0044, 0x0045, 0x0050, 0x0051, 0x0054, 0x0055,
+        0x0100, 0x0101, 0x0104, 0x0105, 0x0110, 0x0111, 0x0114, 0x0115,
+        0x0140, 0x0141, 0x0144, 0x0145, 0x0150, 0x0151, 0x0154, 0x0155,
+        0x0400, 0x0401, 0x0404, 0x0405, 0x0410, 0x0411, 0x0414, 0x0415,
+        0x0440, 0x0441, 0x0444, 0x0445, 0x0450, 0x0451, 0x0454, 0x0455,
+        0x0500, 0x0501, 0x0504, 0x0505, 0x0510, 0x0511, 0x0514, 0x0515,
+        0x0540, 0x0541, 0x0544, 0x0545, 0x0550, 0x0551, 0x0554, 0x0555,
+        0x1000, 0x1001, 0x1004, 0x1005, 0x1010, 0x1011, 0x1014, 0x1015,
+        0x1040, 0x1041, 0x1044, 0x1045, 0x1050, 0x1051, 0x1054, 0x1055,
+        0x1100, 0x1101, 0x1104, 0x1105, 0x1110, 0x1111, 0x1114, 0x1115,
+        0x1140, 0x1141, 0x1144, 0x1145, 0x1150, 0x1151, 0x1154, 0x1155,
+        0x1400, 0x1401, 0x1404, 0x1405, 0x1410, 0x1411, 0x1414, 0x1415,
+        0x1440, 0x1441, 0x1444, 0x1445, 0x1450, 0x1451, 0x1454, 0x1455,
+        0x1500, 0x1501, 0x1504, 0x1505, 0x1510, 0x1511, 0x1514, 0x1515,
+        0x1540, 0x1541, 0x1544, 0x1545, 0x1550, 0x1551, 0x1554, 0x1555,
+        0x4000, 0x4001, 0x4004, 0x4005, 0x4010, 0x4011, 0x4014, 0x4015,
+        0x4040, 0x4041, 0x4044, 0x4045, 0x4050, 0x4051, 0x4054, 0x4055,
+        0x4100, 0x4101, 0x4104, 0x4105, 0x4110, 0x4111, 0x4114, 0x4115,
+        0x4140, 0x4141, 0x4144, 0x4145, 0x4150, 0x4151, 0x4154, 0x4155,
+        0x4400, 0x4401, 0x4404, 0x4405, 0x4410, 0x4411, 0x4414, 0x4415,
+        0x4440, 0x4441, 0x4444, 0x4445, 0x4450, 0x4451, 0x4454, 0x4455,
+        0x4500, 0x4501, 0x4504, 0x4505, 0x4510, 0x4511, 0x4514, 0x4515,
+        0x4540, 0x4541, 0x4544, 0x4545, 0x4550, 0x4551, 0x4554, 0x4555,
+        0x5000, 0x5001, 0x5004, 0x5005, 0x5010, 0x5011, 0x5014, 0x5015,
+        0x5040, 0x5041, 0x5044, 0x5045, 0x5050, 0x5051, 0x5054, 0x5055,
+        0x5100, 0x5101, 0x5104, 0x5105, 0x5110, 0x5111, 0x5114, 0x5115,
+        0x5140, 0x5141, 0x5144, 0x5145, 0x5150, 0x5151, 0x5154, 0x5155,
+        0x5400, 0x5401, 0x5404, 0x5405, 0x5410, 0x5411, 0x5414, 0x5415,
+        0x5440, 0x5441, 0x5444, 0x5445, 0x5450, 0x5451, 0x5454, 0x5455,
+        0x5500, 0x5501, 0x5504, 0x5505, 0x5510, 0x5511, 0x5514, 0x5515,
+        0x5540, 0x5541, 0x5544, 0x5545, 0x5550, 0x5551, 0x5554, 0x5555
+    };
+
+    /*
+     * This expands 7 bit indices into 21 bit contents (high bit 18), by inserting 0s between bits.
+     */
+    private static final int[] INTERLEAVE3_TABLE = new  int[]
+    {
+        0x00000, 0x00001, 0x00008, 0x00009, 0x00040, 0x00041, 0x00048, 0x00049,
+        0x00200, 0x00201, 0x00208, 0x00209, 0x00240, 0x00241, 0x00248, 0x00249,
+        0x01000, 0x01001, 0x01008, 0x01009, 0x01040, 0x01041, 0x01048, 0x01049,
+        0x01200, 0x01201, 0x01208, 0x01209, 0x01240, 0x01241, 0x01248, 0x01249,
+        0x08000, 0x08001, 0x08008, 0x08009, 0x08040, 0x08041, 0x08048, 0x08049,
+        0x08200, 0x08201, 0x08208, 0x08209, 0x08240, 0x08241, 0x08248, 0x08249,
+        0x09000, 0x09001, 0x09008, 0x09009, 0x09040, 0x09041, 0x09048, 0x09049,
+        0x09200, 0x09201, 0x09208, 0x09209, 0x09240, 0x09241, 0x09248, 0x09249,
+        0x40000, 0x40001, 0x40008, 0x40009, 0x40040, 0x40041, 0x40048, 0x40049,
+        0x40200, 0x40201, 0x40208, 0x40209, 0x40240, 0x40241, 0x40248, 0x40249,
+        0x41000, 0x41001, 0x41008, 0x41009, 0x41040, 0x41041, 0x41048, 0x41049,
+        0x41200, 0x41201, 0x41208, 0x41209, 0x41240, 0x41241, 0x41248, 0x41249,
+        0x48000, 0x48001, 0x48008, 0x48009, 0x48040, 0x48041, 0x48048, 0x48049,
+        0x48200, 0x48201, 0x48208, 0x48209, 0x48240, 0x48241, 0x48248, 0x48249,
+        0x49000, 0x49001, 0x49008, 0x49009, 0x49040, 0x49041, 0x49048, 0x49049,
+        0x49200, 0x49201, 0x49208, 0x49209, 0x49240, 0x49241, 0x49248, 0x49249
+    };
+
+    /*
+     * This expands 8 bit indices into 32 bit contents (high bit 28), by inserting 0s between bits.
+     */
+    private static final int[] INTERLEAVE4_TABLE = new int[]
+    {
+        0x00000000, 0x00000001, 0x00000010, 0x00000011, 0x00000100, 0x00000101, 0x00000110, 0x00000111,
+        0x00001000, 0x00001001, 0x00001010, 0x00001011, 0x00001100, 0x00001101, 0x00001110, 0x00001111,
+        0x00010000, 0x00010001, 0x00010010, 0x00010011, 0x00010100, 0x00010101, 0x00010110, 0x00010111,
+        0x00011000, 0x00011001, 0x00011010, 0x00011011, 0x00011100, 0x00011101, 0x00011110, 0x00011111,
+        0x00100000, 0x00100001, 0x00100010, 0x00100011, 0x00100100, 0x00100101, 0x00100110, 0x00100111,
+        0x00101000, 0x00101001, 0x00101010, 0x00101011, 0x00101100, 0x00101101, 0x00101110, 0x00101111,
+        0x00110000, 0x00110001, 0x00110010, 0x00110011, 0x00110100, 0x00110101, 0x00110110, 0x00110111,
+        0x00111000, 0x00111001, 0x00111010, 0x00111011, 0x00111100, 0x00111101, 0x00111110, 0x00111111,
+        0x01000000, 0x01000001, 0x01000010, 0x01000011, 0x01000100, 0x01000101, 0x01000110, 0x01000111,
+        0x01001000, 0x01001001, 0x01001010, 0x01001011, 0x01001100, 0x01001101, 0x01001110, 0x01001111,
+        0x01010000, 0x01010001, 0x01010010, 0x01010011, 0x01010100, 0x01010101, 0x01010110, 0x01010111,
+        0x01011000, 0x01011001, 0x01011010, 0x01011011, 0x01011100, 0x01011101, 0x01011110, 0x01011111,
+        0x01100000, 0x01100001, 0x01100010, 0x01100011, 0x01100100, 0x01100101, 0x01100110, 0x01100111,
+        0x01101000, 0x01101001, 0x01101010, 0x01101011, 0x01101100, 0x01101101, 0x01101110, 0x01101111,
+        0x01110000, 0x01110001, 0x01110010, 0x01110011, 0x01110100, 0x01110101, 0x01110110, 0x01110111,
+        0x01111000, 0x01111001, 0x01111010, 0x01111011, 0x01111100, 0x01111101, 0x01111110, 0x01111111,
+        0x10000000, 0x10000001, 0x10000010, 0x10000011, 0x10000100, 0x10000101, 0x10000110, 0x10000111,
+        0x10001000, 0x10001001, 0x10001010, 0x10001011, 0x10001100, 0x10001101, 0x10001110, 0x10001111,
+        0x10010000, 0x10010001, 0x10010010, 0x10010011, 0x10010100, 0x10010101, 0x10010110, 0x10010111,
+        0x10011000, 0x10011001, 0x10011010, 0x10011011, 0x10011100, 0x10011101, 0x10011110, 0x10011111,
+        0x10100000, 0x10100001, 0x10100010, 0x10100011, 0x10100100, 0x10100101, 0x10100110, 0x10100111,
+        0x10101000, 0x10101001, 0x10101010, 0x10101011, 0x10101100, 0x10101101, 0x10101110, 0x10101111,
+        0x10110000, 0x10110001, 0x10110010, 0x10110011, 0x10110100, 0x10110101, 0x10110110, 0x10110111,
+        0x10111000, 0x10111001, 0x10111010, 0x10111011, 0x10111100, 0x10111101, 0x10111110, 0x10111111,
+        0x11000000, 0x11000001, 0x11000010, 0x11000011, 0x11000100, 0x11000101, 0x11000110, 0x11000111,
+        0x11001000, 0x11001001, 0x11001010, 0x11001011, 0x11001100, 0x11001101, 0x11001110, 0x11001111,
+        0x11010000, 0x11010001, 0x11010010, 0x11010011, 0x11010100, 0x11010101, 0x11010110, 0x11010111,
+        0x11011000, 0x11011001, 0x11011010, 0x11011011, 0x11011100, 0x11011101, 0x11011110, 0x11011111,
+        0x11100000, 0x11100001, 0x11100010, 0x11100011, 0x11100100, 0x11100101, 0x11100110, 0x11100111,
+        0x11101000, 0x11101001, 0x11101010, 0x11101011, 0x11101100, 0x11101101, 0x11101110, 0x11101111,
+        0x11110000, 0x11110001, 0x11110010, 0x11110011, 0x11110100, 0x11110101, 0x11110110, 0x11110111,
+        0x11111000, 0x11111001, 0x11111010, 0x11111011, 0x11111100, 0x11111101, 0x11111110, 0x11111111
+    };
+
+    /*
+     * This expands 7 bit indices into 35 bit contents (high bit 30), by inserting 0s between bits.
+     */
+    private static final int[] INTERLEAVE5_TABLE = new int[] {
+        0x00000000, 0x00000001, 0x00000020, 0x00000021, 0x00000400, 0x00000401, 0x00000420, 0x00000421,
+        0x00008000, 0x00008001, 0x00008020, 0x00008021, 0x00008400, 0x00008401, 0x00008420, 0x00008421,
+        0x00100000, 0x00100001, 0x00100020, 0x00100021, 0x00100400, 0x00100401, 0x00100420, 0x00100421,
+        0x00108000, 0x00108001, 0x00108020, 0x00108021, 0x00108400, 0x00108401, 0x00108420, 0x00108421,
+        0x02000000, 0x02000001, 0x02000020, 0x02000021, 0x02000400, 0x02000401, 0x02000420, 0x02000421,
+        0x02008000, 0x02008001, 0x02008020, 0x02008021, 0x02008400, 0x02008401, 0x02008420, 0x02008421,
+        0x02100000, 0x02100001, 0x02100020, 0x02100021, 0x02100400, 0x02100401, 0x02100420, 0x02100421,
+        0x02108000, 0x02108001, 0x02108020, 0x02108021, 0x02108400, 0x02108401, 0x02108420, 0x02108421,
+        0x40000000, 0x40000001, 0x40000020, 0x40000021, 0x40000400, 0x40000401, 0x40000420, 0x40000421,
+        0x40008000, 0x40008001, 0x40008020, 0x40008021, 0x40008400, 0x40008401, 0x40008420, 0x40008421,
+        0x40100000, 0x40100001, 0x40100020, 0x40100021, 0x40100400, 0x40100401, 0x40100420, 0x40100421,
+        0x40108000, 0x40108001, 0x40108020, 0x40108021, 0x40108400, 0x40108401, 0x40108420, 0x40108421,
+        0x42000000, 0x42000001, 0x42000020, 0x42000021, 0x42000400, 0x42000401, 0x42000420, 0x42000421,
+        0x42008000, 0x42008001, 0x42008020, 0x42008021, 0x42008400, 0x42008401, 0x42008420, 0x42008421,
+        0x42100000, 0x42100001, 0x42100020, 0x42100021, 0x42100400, 0x42100401, 0x42100420, 0x42100421,
+        0x42108000, 0x42108001, 0x42108020, 0x42108021, 0x42108400, 0x42108401, 0x42108420, 0x42108421
+    };
+
+    /*
+     * This expands 9 bit indices into 63 bit (long) contents (high bit 56), by inserting 0s between bits.
+     */
+    private static final long[] INTERLEAVE7_TABLE = new long[]
+    {
+        0x0000000000000000L, 0x0000000000000001L, 0x0000000000000080L, 0x0000000000000081L,
+        0x0000000000004000L, 0x0000000000004001L, 0x0000000000004080L, 0x0000000000004081L,
+        0x0000000000200000L, 0x0000000000200001L, 0x0000000000200080L, 0x0000000000200081L,
+        0x0000000000204000L, 0x0000000000204001L, 0x0000000000204080L, 0x0000000000204081L,
+        0x0000000010000000L, 0x0000000010000001L, 0x0000000010000080L, 0x0000000010000081L,
+        0x0000000010004000L, 0x0000000010004001L, 0x0000000010004080L, 0x0000000010004081L,
+        0x0000000010200000L, 0x0000000010200001L, 0x0000000010200080L, 0x0000000010200081L,
+        0x0000000010204000L, 0x0000000010204001L, 0x0000000010204080L, 0x0000000010204081L,
+        0x0000000800000000L, 0x0000000800000001L, 0x0000000800000080L, 0x0000000800000081L,
+        0x0000000800004000L, 0x0000000800004001L, 0x0000000800004080L, 0x0000000800004081L,
+        0x0000000800200000L, 0x0000000800200001L, 0x0000000800200080L, 0x0000000800200081L,
+        0x0000000800204000L, 0x0000000800204001L, 0x0000000800204080L, 0x0000000800204081L,
+        0x0000000810000000L, 0x0000000810000001L, 0x0000000810000080L, 0x0000000810000081L,
+        0x0000000810004000L, 0x0000000810004001L, 0x0000000810004080L, 0x0000000810004081L,
+        0x0000000810200000L, 0x0000000810200001L, 0x0000000810200080L, 0x0000000810200081L,
+        0x0000000810204000L, 0x0000000810204001L, 0x0000000810204080L, 0x0000000810204081L,
+        0x0000040000000000L, 0x0000040000000001L, 0x0000040000000080L, 0x0000040000000081L,
+        0x0000040000004000L, 0x0000040000004001L, 0x0000040000004080L, 0x0000040000004081L,
+        0x0000040000200000L, 0x0000040000200001L, 0x0000040000200080L, 0x0000040000200081L,
+        0x0000040000204000L, 0x0000040000204001L, 0x0000040000204080L, 0x0000040000204081L,
+        0x0000040010000000L, 0x0000040010000001L, 0x0000040010000080L, 0x0000040010000081L,
+        0x0000040010004000L, 0x0000040010004001L, 0x0000040010004080L, 0x0000040010004081L,
+        0x0000040010200000L, 0x0000040010200001L, 0x0000040010200080L, 0x0000040010200081L,
+        0x0000040010204000L, 0x0000040010204001L, 0x0000040010204080L, 0x0000040010204081L,
+        0x0000040800000000L, 0x0000040800000001L, 0x0000040800000080L, 0x0000040800000081L,
+        0x0000040800004000L, 0x0000040800004001L, 0x0000040800004080L, 0x0000040800004081L,
+        0x0000040800200000L, 0x0000040800200001L, 0x0000040800200080L, 0x0000040800200081L,
+        0x0000040800204000L, 0x0000040800204001L, 0x0000040800204080L, 0x0000040800204081L,
+        0x0000040810000000L, 0x0000040810000001L, 0x0000040810000080L, 0x0000040810000081L,
+        0x0000040810004000L, 0x0000040810004001L, 0x0000040810004080L, 0x0000040810004081L,
+        0x0000040810200000L, 0x0000040810200001L, 0x0000040810200080L, 0x0000040810200081L,
+        0x0000040810204000L, 0x0000040810204001L, 0x0000040810204080L, 0x0000040810204081L,
+        0x0002000000000000L, 0x0002000000000001L, 0x0002000000000080L, 0x0002000000000081L,
+        0x0002000000004000L, 0x0002000000004001L, 0x0002000000004080L, 0x0002000000004081L,
+        0x0002000000200000L, 0x0002000000200001L, 0x0002000000200080L, 0x0002000000200081L,
+        0x0002000000204000L, 0x0002000000204001L, 0x0002000000204080L, 0x0002000000204081L,
+        0x0002000010000000L, 0x0002000010000001L, 0x0002000010000080L, 0x0002000010000081L,
+        0x0002000010004000L, 0x0002000010004001L, 0x0002000010004080L, 0x0002000010004081L,
+        0x0002000010200000L, 0x0002000010200001L, 0x0002000010200080L, 0x0002000010200081L,
+        0x0002000010204000L, 0x0002000010204001L, 0x0002000010204080L, 0x0002000010204081L,
+        0x0002000800000000L, 0x0002000800000001L, 0x0002000800000080L, 0x0002000800000081L,
+        0x0002000800004000L, 0x0002000800004001L, 0x0002000800004080L, 0x0002000800004081L,
+        0x0002000800200000L, 0x0002000800200001L, 0x0002000800200080L, 0x0002000800200081L,
+        0x0002000800204000L, 0x0002000800204001L, 0x0002000800204080L, 0x0002000800204081L,
+        0x0002000810000000L, 0x0002000810000001L, 0x0002000810000080L, 0x0002000810000081L,
+        0x0002000810004000L, 0x0002000810004001L, 0x0002000810004080L, 0x0002000810004081L,
+        0x0002000810200000L, 0x0002000810200001L, 0x0002000810200080L, 0x0002000810200081L,
+        0x0002000810204000L, 0x0002000810204001L, 0x0002000810204080L, 0x0002000810204081L,
+        0x0002040000000000L, 0x0002040000000001L, 0x0002040000000080L, 0x0002040000000081L,
+        0x0002040000004000L, 0x0002040000004001L, 0x0002040000004080L, 0x0002040000004081L,
+        0x0002040000200000L, 0x0002040000200001L, 0x0002040000200080L, 0x0002040000200081L,
+        0x0002040000204000L, 0x0002040000204001L, 0x0002040000204080L, 0x0002040000204081L,
+        0x0002040010000000L, 0x0002040010000001L, 0x0002040010000080L, 0x0002040010000081L,
+        0x0002040010004000L, 0x0002040010004001L, 0x0002040010004080L, 0x0002040010004081L,
+        0x0002040010200000L, 0x0002040010200001L, 0x0002040010200080L, 0x0002040010200081L,
+        0x0002040010204000L, 0x0002040010204001L, 0x0002040010204080L, 0x0002040010204081L,
+        0x0002040800000000L, 0x0002040800000001L, 0x0002040800000080L, 0x0002040800000081L,
+        0x0002040800004000L, 0x0002040800004001L, 0x0002040800004080L, 0x0002040800004081L,
+        0x0002040800200000L, 0x0002040800200001L, 0x0002040800200080L, 0x0002040800200081L,
+        0x0002040800204000L, 0x0002040800204001L, 0x0002040800204080L, 0x0002040800204081L,
+        0x0002040810000000L, 0x0002040810000001L, 0x0002040810000080L, 0x0002040810000081L,
+        0x0002040810004000L, 0x0002040810004001L, 0x0002040810004080L, 0x0002040810004081L,
+        0x0002040810200000L, 0x0002040810200001L, 0x0002040810200080L, 0x0002040810200081L,
+        0x0002040810204000L, 0x0002040810204001L, 0x0002040810204080L, 0x0002040810204081L,
+        0x0100000000000000L, 0x0100000000000001L, 0x0100000000000080L, 0x0100000000000081L,
+        0x0100000000004000L, 0x0100000000004001L, 0x0100000000004080L, 0x0100000000004081L,
+        0x0100000000200000L, 0x0100000000200001L, 0x0100000000200080L, 0x0100000000200081L,
+        0x0100000000204000L, 0x0100000000204001L, 0x0100000000204080L, 0x0100000000204081L,
+        0x0100000010000000L, 0x0100000010000001L, 0x0100000010000080L, 0x0100000010000081L,
+        0x0100000010004000L, 0x0100000010004001L, 0x0100000010004080L, 0x0100000010004081L,
+        0x0100000010200000L, 0x0100000010200001L, 0x0100000010200080L, 0x0100000010200081L,
+        0x0100000010204000L, 0x0100000010204001L, 0x0100000010204080L, 0x0100000010204081L,
+        0x0100000800000000L, 0x0100000800000001L, 0x0100000800000080L, 0x0100000800000081L,
+        0x0100000800004000L, 0x0100000800004001L, 0x0100000800004080L, 0x0100000800004081L,
+        0x0100000800200000L, 0x0100000800200001L, 0x0100000800200080L, 0x0100000800200081L,
+        0x0100000800204000L, 0x0100000800204001L, 0x0100000800204080L, 0x0100000800204081L,
+        0x0100000810000000L, 0x0100000810000001L, 0x0100000810000080L, 0x0100000810000081L,
+        0x0100000810004000L, 0x0100000810004001L, 0x0100000810004080L, 0x0100000810004081L,
+        0x0100000810200000L, 0x0100000810200001L, 0x0100000810200080L, 0x0100000810200081L,
+        0x0100000810204000L, 0x0100000810204001L, 0x0100000810204080L, 0x0100000810204081L,
+        0x0100040000000000L, 0x0100040000000001L, 0x0100040000000080L, 0x0100040000000081L,
+        0x0100040000004000L, 0x0100040000004001L, 0x0100040000004080L, 0x0100040000004081L,
+        0x0100040000200000L, 0x0100040000200001L, 0x0100040000200080L, 0x0100040000200081L,
+        0x0100040000204000L, 0x0100040000204001L, 0x0100040000204080L, 0x0100040000204081L,
+        0x0100040010000000L, 0x0100040010000001L, 0x0100040010000080L, 0x0100040010000081L,
+        0x0100040010004000L, 0x0100040010004001L, 0x0100040010004080L, 0x0100040010004081L,
+        0x0100040010200000L, 0x0100040010200001L, 0x0100040010200080L, 0x0100040010200081L,
+        0x0100040010204000L, 0x0100040010204001L, 0x0100040010204080L, 0x0100040010204081L,
+        0x0100040800000000L, 0x0100040800000001L, 0x0100040800000080L, 0x0100040800000081L,
+        0x0100040800004000L, 0x0100040800004001L, 0x0100040800004080L, 0x0100040800004081L,
+        0x0100040800200000L, 0x0100040800200001L, 0x0100040800200080L, 0x0100040800200081L,
+        0x0100040800204000L, 0x0100040800204001L, 0x0100040800204080L, 0x0100040800204081L,
+        0x0100040810000000L, 0x0100040810000001L, 0x0100040810000080L, 0x0100040810000081L,
+        0x0100040810004000L, 0x0100040810004001L, 0x0100040810004080L, 0x0100040810004081L,
+        0x0100040810200000L, 0x0100040810200001L, 0x0100040810200080L, 0x0100040810200081L,
+        0x0100040810204000L, 0x0100040810204001L, 0x0100040810204080L, 0x0100040810204081L,
+        0x0102000000000000L, 0x0102000000000001L, 0x0102000000000080L, 0x0102000000000081L,
+        0x0102000000004000L, 0x0102000000004001L, 0x0102000000004080L, 0x0102000000004081L,
+        0x0102000000200000L, 0x0102000000200001L, 0x0102000000200080L, 0x0102000000200081L,
+        0x0102000000204000L, 0x0102000000204001L, 0x0102000000204080L, 0x0102000000204081L,
+        0x0102000010000000L, 0x0102000010000001L, 0x0102000010000080L, 0x0102000010000081L,
+        0x0102000010004000L, 0x0102000010004001L, 0x0102000010004080L, 0x0102000010004081L,
+        0x0102000010200000L, 0x0102000010200001L, 0x0102000010200080L, 0x0102000010200081L,
+        0x0102000010204000L, 0x0102000010204001L, 0x0102000010204080L, 0x0102000010204081L,
+        0x0102000800000000L, 0x0102000800000001L, 0x0102000800000080L, 0x0102000800000081L,
+        0x0102000800004000L, 0x0102000800004001L, 0x0102000800004080L, 0x0102000800004081L,
+        0x0102000800200000L, 0x0102000800200001L, 0x0102000800200080L, 0x0102000800200081L,
+        0x0102000800204000L, 0x0102000800204001L, 0x0102000800204080L, 0x0102000800204081L,
+        0x0102000810000000L, 0x0102000810000001L, 0x0102000810000080L, 0x0102000810000081L,
+        0x0102000810004000L, 0x0102000810004001L, 0x0102000810004080L, 0x0102000810004081L,
+        0x0102000810200000L, 0x0102000810200001L, 0x0102000810200080L, 0x0102000810200081L,
+        0x0102000810204000L, 0x0102000810204001L, 0x0102000810204080L, 0x0102000810204081L,
+        0x0102040000000000L, 0x0102040000000001L, 0x0102040000000080L, 0x0102040000000081L,
+        0x0102040000004000L, 0x0102040000004001L, 0x0102040000004080L, 0x0102040000004081L,
+        0x0102040000200000L, 0x0102040000200001L, 0x0102040000200080L, 0x0102040000200081L,
+        0x0102040000204000L, 0x0102040000204001L, 0x0102040000204080L, 0x0102040000204081L,
+        0x0102040010000000L, 0x0102040010000001L, 0x0102040010000080L, 0x0102040010000081L,
+        0x0102040010004000L, 0x0102040010004001L, 0x0102040010004080L, 0x0102040010004081L,
+        0x0102040010200000L, 0x0102040010200001L, 0x0102040010200080L, 0x0102040010200081L,
+        0x0102040010204000L, 0x0102040010204001L, 0x0102040010204080L, 0x0102040010204081L,
+        0x0102040800000000L, 0x0102040800000001L, 0x0102040800000080L, 0x0102040800000081L,
+        0x0102040800004000L, 0x0102040800004001L, 0x0102040800004080L, 0x0102040800004081L,
+        0x0102040800200000L, 0x0102040800200001L, 0x0102040800200080L, 0x0102040800200081L,
+        0x0102040800204000L, 0x0102040800204001L, 0x0102040800204080L, 0x0102040800204081L,
+        0x0102040810000000L, 0x0102040810000001L, 0x0102040810000080L, 0x0102040810000081L,
+        0x0102040810004000L, 0x0102040810004001L, 0x0102040810004080L, 0x0102040810004081L,
+        0x0102040810200000L, 0x0102040810200001L, 0x0102040810200080L, 0x0102040810200081L,
+        0x0102040810204000L, 0x0102040810204001L, 0x0102040810204080L, 0x0102040810204081L
+    };
+
+    // For toString(); must have length 64
+    private static final String ZEROES = "0000000000000000000000000000000000000000000000000000000000000000";
+
+    final static byte[] bitLengths =
+    {
+        0, 1, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4,
+        5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+        6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8,
+        8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
+    };
+
+    // TODO make m fixed for the LongArray, and hence compute T once and for all
+
+    private long[] m_ints;
+
+    public LongArray(int intLen)
+    {
+        m_ints = new long[intLen];
+    }
+
+    public LongArray(long[] ints)
+    {
+        m_ints = ints;
+    }
+
+    public LongArray(long[] ints, int off, int len)
+    {
+        if (off == 0 && len == ints.length)
+        {
+            m_ints = ints;
+        }
+        else
+        {
+            m_ints = new long[len];
+            System.arraycopy(ints, off, m_ints, 0, len);
+        }
+    }
+
+    public LongArray(BigInteger bigInt)
+    {
+        if (bigInt == null || bigInt.signum() < 0)
+        {
+            throw new IllegalArgumentException("invalid F2m field value");
+        }
+
+        if (bigInt.signum() == 0)
+        {
+            m_ints = new long[] { 0L };
+            return;
+        }
+
+        byte[] barr = bigInt.toByteArray();
+        int barrLen = barr.length;
+        int barrStart = 0;
+        if (barr[0] == 0)
+        {
+            // First byte is 0 to enforce highest (=sign) bit is zero.
+            // In this case ignore barr[0].
+            barrLen--;
+            barrStart = 1;
+        }
+        int intLen = (barrLen + 7) / 8;
+        m_ints = new long[intLen];
+
+        int iarrJ = intLen - 1;
+        int rem = barrLen % 8 + barrStart;
+        long temp = 0;
+        int barrI = barrStart;
+        if (barrStart < rem)
+        {
+            for (; barrI < rem; barrI++)
+            {
+                temp <<= 8;
+                int barrBarrI = barr[barrI] & 0xFF;
+                temp |= barrBarrI;
+            }
+            m_ints[iarrJ--] = temp;
+        }
+
+        for (; iarrJ >= 0; iarrJ--)
+        {
+            temp = 0;
+            for (int i = 0; i < 8; i++)
+            {
+                temp <<= 8;
+                int barrBarrI = barr[barrI++] & 0xFF;
+                temp |= barrBarrI;
+            }
+            m_ints[iarrJ] = temp;
+        }
+    }
+
+    public boolean isZero()
+    {
+        long[] a = m_ints;
+        for (int i = 0; i < a.length; ++i)
+        {
+            if (a[i] != 0L)
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int getUsedLength()
+    {
+        return getUsedLengthFrom(m_ints.length);
+    }
+
+    public int getUsedLengthFrom(int from)
+    {
+        long[] a = m_ints;
+        from = Math.min(from, a.length);
+
+        if (from < 1)
+        {
+            return 0;
+        }
+
+        // Check if first element will act as sentinel
+        if (a[0] != 0)
+        {
+            while (a[--from] == 0)
+            {
+            }
+            return from + 1;
+        }
+
+        do
+        {
+            if (a[--from] != 0)
+            {
+                return from + 1;
+            }
+        }
+        while (from > 0);
+
+        return 0;
+    }
+
+    public int degree()
+    {
+        int i = m_ints.length;
+        long w;
+        do
+        {
+            if (i == 0)
+            {
+                return 0;
+            }
+            w = m_ints[--i];
+        }
+        while (w == 0);
+
+        return (i << 6) + bitLength(w);
+    }
+
+    private int degreeFrom(int limit)
+    {
+        int i = (limit + 62) >>> 6;
+        long w;
+        do
+        {
+            if (i == 0)
+            {
+                return 0;
+            }
+            w = m_ints[--i];
+        }
+        while (w == 0);
+
+        return (i << 6) + bitLength(w);
+    }
+
+//    private int lowestCoefficient()
+//    {
+//        for (int i = 0; i < m_ints.length; ++i)
+//        {
+//            long mi = m_ints[i];
+//            if (mi != 0)
+//            {
+//                int j = 0;
+//                while ((mi & 0xFFL) == 0)
+//                {
+//                    j += 8;
+//                    mi >>>= 8;
+//                }
+//                while ((mi & 1L) == 0)
+//                {
+//                    ++j;
+//                    mi >>>= 1;
+//                }
+//                return (i << 6) + j;
+//            }
+//        }
+//        return -1;
+//    }
+
+    private static int bitLength(long w)
+    {
+        int u = (int)(w >>> 32), b;
+        if (u == 0)
+        {
+            u = (int)w;
+            b = 0;
+        }
+        else
+        {
+            b = 32;
+        }
+
+        int t = u >>> 16, k;
+        if (t == 0)
+        {
+            t = u >>> 8;
+            k = (t == 0) ? bitLengths[u] : 8 + bitLengths[t];
+        }
+        else
+        {
+            int v = t >>> 8;
+            k = (v == 0) ? 16 + bitLengths[t] : 24 + bitLengths[v];
+        }
+
+        return b + k;
+    }
+
+    private long[] resizedInts(int newLen)
+    {
+        long[] newInts = new long[newLen];
+        System.arraycopy(m_ints, 0, newInts, 0, Math.min(m_ints.length, newLen));
+        return newInts;
+    }
+
+    public BigInteger toBigInteger()
+    {
+        int usedLen = getUsedLength();
+        if (usedLen == 0)
+        {
+            return ECConstants.ZERO;
+        }
+
+        long highestInt = m_ints[usedLen - 1];
+        byte[] temp = new byte[8];
+        int barrI = 0;
+        boolean trailingZeroBytesDone = false;
+        for (int j = 7; j >= 0; j--)
+        {
+            byte thisByte = (byte)(highestInt >>> (8 * j));
+            if (trailingZeroBytesDone || (thisByte != 0))
+            {
+                trailingZeroBytesDone = true;
+                temp[barrI++] = thisByte;
+            }
+        }
+
+        int barrLen = 8 * (usedLen - 1) + barrI;
+        byte[] barr = new byte[barrLen];
+        for (int j = 0; j < barrI; j++)
+        {
+            barr[j] = temp[j];
+        }
+        // Highest value int is done now
+
+        for (int iarrJ = usedLen - 2; iarrJ >= 0; iarrJ--)
+        {
+            long mi = m_ints[iarrJ];
+            for (int j = 7; j >= 0; j--)
+            {
+                barr[barrI++] = (byte)(mi >>> (8 * j));
+            }
+        }
+        return new BigInteger(1, barr);
+    }
+
+//    private static long shiftUp(long[] x, int xOff, int count)
+//    {
+//        long prev = 0;
+//        for (int i = 0; i < count; ++i)
+//        {
+//            long next = x[xOff + i];
+//            x[xOff + i] = (next << 1) | prev;
+//            prev = next >>> 63;
+//        }
+//        return prev;
+//    }
+
+    private static long shiftUp(long[] x, int xOff, int count, int shift)
+    {
+        int shiftInv = 64 - shift;
+        long prev = 0;
+        for (int i = 0; i < count; ++i)
+        {
+            long next = x[xOff + i];
+            x[xOff + i] = (next << shift) | prev;
+            prev = next >>> shiftInv;
+        }
+        return prev;
+    }
+
+    private static long shiftUp(long[] x, int xOff, long[] z, int zOff, int count, int shift)
+    {
+        int shiftInv = 64 - shift;
+        long prev = 0;
+        for (int i = 0; i < count; ++i)
+        {
+            long next = x[xOff + i];
+            z[zOff + i] = (next << shift) | prev;
+            prev = next >>> shiftInv;
+        }
+        return prev;
+    }
+
+    public LongArray addOne()
+    {
+        if (m_ints.length == 0)
+        {
+            return new LongArray(new long[]{ 1L });
+        }
+
+        int resultLen = Math.max(1, getUsedLength());
+        long[] ints = resizedInts(resultLen);
+        ints[0] ^= 1L;
+        return new LongArray(ints);
+    }
+
+//    private void addShiftedByBits(LongArray other, int bits)
+//    {
+//        int words = bits >>> 6;
+//        int shift = bits & 0x3F;
+//
+//        if (shift == 0)
+//        {
+//            addShiftedByWords(other, words);
+//            return;
+//        }
+//
+//        int otherUsedLen = other.getUsedLength();
+//        if (otherUsedLen == 0)
+//        {
+//            return;
+//        }
+//
+//        int minLen = otherUsedLen + words + 1;
+//        if (minLen > m_ints.length)
+//        {
+//            m_ints = resizedInts(minLen);
+//        }
+//
+//        long carry = addShiftedByBits(m_ints, words, other.m_ints, 0, otherUsedLen, shift);
+//        m_ints[otherUsedLen + words] ^= carry;
+//    }
+
+    private void addShiftedByBitsSafe(LongArray other, int otherDegree, int bits)
+    {
+        int otherLen = (otherDegree + 63) >>> 6;
+
+        int words = bits >>> 6;
+        int shift = bits & 0x3F;
+
+        if (shift == 0)
+        {
+            add(m_ints, words, other.m_ints, 0, otherLen);
+            return;
+        }
+
+        long carry = addShiftedUp(m_ints, words, other.m_ints, 0, otherLen, shift);
+        if (carry != 0L)
+        {
+            m_ints[otherLen + words] ^= carry;
+        }
+    }
+
+    private static long addShiftedUp(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+    {
+        int shiftInv = 64 - shift;
+        long prev = 0;
+        for (int i = 0; i < count; ++i)
+        {
+            long next = y[yOff + i];
+            x[xOff + i] ^= (next << shift) | prev;
+            prev = next >>> shiftInv;
+        }
+        return prev;
+    }
+
+    private static long addShiftedDown(long[] x, int xOff, long[] y, int yOff, int count, int shift)
+    {
+        int shiftInv = 64 - shift;
+        long prev = 0;
+        int i = count;
+        while (--i >= 0)
+        {
+            long next = y[yOff + i];
+            x[xOff + i] ^= (next >>> shift) | prev;
+            prev = next << shiftInv;
+        }
+        return prev;
+    }
+
+    public void addShiftedByWords(LongArray other, int words)
+    {
+        int otherUsedLen = other.getUsedLength();
+        if (otherUsedLen == 0)
+        {
+            return;
+        }
+
+        int minLen = otherUsedLen + words;
+        if (minLen > m_ints.length)
+        {
+            m_ints = resizedInts(minLen);
+        }
+
+        add(m_ints, words, other.m_ints, 0, otherUsedLen);
+    }
+
+    private static void add(long[] x, int xOff, long[] y, int yOff, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            x[xOff + i] ^= y[yOff + i];
+        }
+    }
+
+    private static void add(long[] x, int xOff, long[] y, int yOff, long[] z, int zOff, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            z[zOff + i] = x[xOff + i] ^ y[yOff + i];
+        }
+    }
+
+    private static void addBoth(long[] x, int xOff, long[] y1, int y1Off, long[] y2, int y2Off, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            x[xOff + i] ^= y1[y1Off + i] ^ y2[y2Off + i];
+        }
+    }
+
+    private static void distribute(long[] x, int src, int dst1, int dst2, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            long v = x[src + i];
+            x[dst1 + i] ^= v;
+            x[dst2 + i] ^= v;
+        }
+    }
+
+    public int getLength()
+    {
+        return m_ints.length;
+    }
+
+    private static void flipWord(long[] buf, int off, int bit, long word)
+    {
+        int n = off + (bit >>> 6);
+        int shift = bit & 0x3F;
+        if (shift == 0)
+        {
+            buf[n] ^= word;
+        }
+        else
+        {
+            buf[n] ^= word << shift;
+            word >>>= (64 - shift);
+            if (word != 0)
+            {
+                buf[++n] ^= word;
+            }
+        }
+    }
+
+//    private static long getWord(long[] buf, int off, int len, int bit)
+//    {
+//        int n = off + (bit >>> 6);
+//        int shift = bit & 0x3F;
+//        if (shift == 0)
+//        {
+//            return buf[n];
+//        }
+//        long result = buf[n] >>> shift;
+//        if (++n < len)
+//        {
+//            result |= buf[n] << (64 - shift);
+//        }
+//        return result;
+//    }
+
+    public boolean testBitZero()
+    {
+        return m_ints.length > 0 && (m_ints[0] & 1L) != 0;
+    }
+
+    private static boolean testBit(long[] buf, int off, int n)
+    {
+        // theInt = n / 64
+        int theInt = n >>> 6;
+        // theBit = n % 64
+        int theBit = n & 0x3F;
+        long tester = 1L << theBit;
+        return (buf[off + theInt] & tester) != 0;
+    }
+
+    private static void flipBit(long[] buf, int off, int n)
+    {
+        // theInt = n / 64
+        int theInt = n >>> 6;
+        // theBit = n % 64
+        int theBit = n & 0x3F;
+        long flipper = 1L << theBit;
+        buf[off + theInt] ^= flipper;
+    }
+
+//    private static void setBit(long[] buf, int off, int n)
+//    {
+//        // theInt = n / 64
+//        int theInt = n >>> 6;
+//        // theBit = n % 64
+//        int theBit = n & 0x3F;
+//        long setter = 1L << theBit;
+//        buf[off + theInt] |= setter;
+//    }
+//
+//    private static void clearBit(long[] buf, int off, int n)
+//    {
+//        // theInt = n / 64
+//        int theInt = n >>> 6;
+//        // theBit = n % 64
+//        int theBit = n & 0x3F;
+//        long setter = 1L << theBit;
+//        buf[off + theInt] &= ~setter;
+//    }
+
+    private static void multiplyWord(long a, long[] b, int bLen, long[] c, int cOff)
+    {
+        if ((a & 1L) != 0L)
+        {
+            add(c, cOff, b, 0, bLen);
+        }
+        int k = 1;
+        while ((a >>>= 1) != 0)
+        {
+            if ((a & 1L) != 0L)
+            {
+                long carry = addShiftedUp(c, cOff, b, 0, bLen, k);
+                if (carry != 0)
+                {
+                    c[cOff + bLen] ^= carry;
+                }
+            }
+            ++k;
+        }
+    }
+
+    public LongArray modMultiplyLD(LongArray other, int m, int[] ks)
+    {
+        /*
+         * Find out the degree of each argument and handle the zero cases
+         */
+        int aDeg = degree();
+        if (aDeg == 0)
+        {
+            return this;
+        }
+        int bDeg = other.degree();
+        if (bDeg == 0)
+        {
+            return other;
+        }
+
+        /*
+         * Swap if necessary so that A is the smaller argument
+         */
+        LongArray A = this, B = other;
+        if (aDeg > bDeg)
+        {
+            A = other; B = this;
+            int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+        }
+
+        /*
+         * Establish the word lengths of the arguments and result
+         */
+        int aLen = (aDeg + 63) >>> 6;
+        int bLen = (bDeg + 63) >>> 6;
+        int cLen = (aDeg + bDeg + 62) >>> 6;
+
+        if (aLen == 1)
+        {
+            long a = A.m_ints[0];
+            if (a == 1L)
+            {
+                return B;
+            }
+
+            /*
+             * Fast path for small A, with performance dependent only on the number of set bits
+             */
+            long[] c = new long[cLen];
+            multiplyWord(a, B.m_ints, bLen, c, 0);
+
+            /*
+             * Reduce the raw answer against the reduction coefficients
+             */
+            return reduceResult(c, 0, cLen, m, ks);
+        }
+
+        /*
+         * Determine if B will get bigger during shifting
+         */
+        int bMax = (bDeg + 7 + 63) >>> 6;
+
+        /*
+         * Lookup table for the offset of each B in the tables
+         */
+        int[] ti = new int[16];
+
+        /*
+         * Precompute table of all 4-bit products of B
+         */
+        long[] T0 = new long[bMax << 4];
+        int tOff = bMax;
+        ti[1] = tOff;
+        System.arraycopy(B.m_ints, 0, T0, tOff, bLen);
+        for (int i = 2; i < 16; ++i)
+        {
+            ti[i] = (tOff += bMax);
+            if ((i & 1) == 0)
+            {
+                shiftUp(T0, tOff >>> 1, T0, tOff, bMax, 1);
+            }
+            else
+            {
+                add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+            }
+        }
+
+        /*
+         * Second table with all 4-bit products of B shifted 4 bits
+         */
+        long[] T1 = new long[T0.length];
+        shiftUp(T0, 0, T1, 0, T0.length, 4);
+//        shiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+        long[] a = A.m_ints;
+        long[] c = new long[cLen];
+
+        int MASK = 0xF;
+
+        /*
+         * Lopez-Dahab algorithm
+         */
+
+        for (int k = 56; k >= 0; k -= 8)
+        {
+            for (int j = 1; j < aLen; j += 2)
+            {
+                int aVal = (int)(a[j] >>> k);
+                int u = aVal & MASK;
+                int v = (aVal >>> 4) & MASK;
+                addBoth(c, j - 1, T0, ti[u], T1, ti[v], bMax);
+            }
+            shiftUp(c, 0, cLen, 8);
+        }
+
+        for (int k = 56; k >= 0; k -= 8)
+        {
+            for (int j = 0; j < aLen; j += 2)
+            {
+                int aVal = (int)(a[j] >>> k);
+                int u = aVal & MASK;
+                int v = (aVal >>> 4) & MASK;
+                addBoth(c, j, T0, ti[u], T1, ti[v], bMax);
+            }
+            if (k > 0)
+            {
+                shiftUp(c, 0, cLen, 8);
+            }
+        }
+
+        /*
+         * Finally the raw answer is collected, reduce it against the reduction coefficients
+         */
+        return reduceResult(c, 0, cLen, m, ks);
+    }
+
+    public LongArray modMultiply(LongArray other, int m, int[] ks)
+    {
+        /*
+         * Find out the degree of each argument and handle the zero cases
+         */
+        int aDeg = degree();
+        if (aDeg == 0)
+        {
+            return this;
+        }
+        int bDeg = other.degree();
+        if (bDeg == 0)
+        {
+            return other;
+        }
+
+        /*
+         * Swap if necessary so that A is the smaller argument
+         */
+        LongArray A = this, B = other;
+        if (aDeg > bDeg)
+        {
+            A = other; B = this;
+            int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+        }
+
+        /*
+         * Establish the word lengths of the arguments and result
+         */
+        int aLen = (aDeg + 63) >>> 6;
+        int bLen = (bDeg + 63) >>> 6;
+        int cLen = (aDeg + bDeg + 62) >>> 6;
+
+        if (aLen == 1)
+        {
+            long a = A.m_ints[0];
+            if (a == 1L)
+            {
+                return B;
+            }
+
+            /*
+             * Fast path for small A, with performance dependent only on the number of set bits
+             */
+            long[] c = new long[cLen];
+            multiplyWord(a, B.m_ints, bLen, c, 0);
+
+            /*
+             * Reduce the raw answer against the reduction coefficients
+             */
+            return reduceResult(c, 0, cLen, m, ks);
+        }
+
+        /*
+         * Determine if B will get bigger during shifting
+         */
+        int bMax = (bDeg + 7 + 63) >>> 6;
+
+        /*
+         * Lookup table for the offset of each B in the tables
+         */
+        int[] ti = new int[16];
+
+        /*
+         * Precompute table of all 4-bit products of B
+         */
+        long[] T0 = new long[bMax << 4];
+        int tOff = bMax;
+        ti[1] = tOff;
+        System.arraycopy(B.m_ints, 0, T0, tOff, bLen);
+        for (int i = 2; i < 16; ++i)
+        {
+            ti[i] = (tOff += bMax);
+            if ((i & 1) == 0)
+            {
+                shiftUp(T0, tOff >>> 1, T0, tOff, bMax, 1);
+            }
+            else
+            {
+                add(T0, bMax, T0, tOff - bMax, T0, tOff, bMax);
+            }
+        }
+
+        /*
+         * Second table with all 4-bit products of B shifted 4 bits
+         */
+        long[] T1 = new long[T0.length];
+        shiftUp(T0, 0, T1, 0, T0.length, 4);
+//        shiftUp(T0, bMax, T1, bMax, tOff, 4);
+
+        long[] a = A.m_ints;
+        long[] c = new long[cLen << 3];
+
+        int MASK = 0xF;
+
+        /*
+         * Lopez-Dahab (Modified) algorithm
+         */
+
+        for (int aPos = 0; aPos < aLen; ++aPos)
+        {
+            long aVal = a[aPos];
+            int cOff = aPos;
+            for (;;)
+            {
+                int u = (int)aVal & MASK;
+                aVal >>>= 4;
+                int v = (int)aVal & MASK;
+                addBoth(c, cOff, T0, ti[u], T1, ti[v], bMax);
+                if ((aVal >>>= 4) == 0L)
+                {
+                    break;
+                }
+                cOff += cLen;
+            }
+        }
+
+        int cOff = c.length;
+        while ((cOff -= cLen) != 0)
+        {
+            addShiftedUp(c, cOff - cLen, c, cOff, cLen, 8);
+        }
+
+        /*
+         * Finally the raw answer is collected, reduce it against the reduction coefficients
+         */
+        return reduceResult(c, 0, cLen, m, ks);
+    }
+
+    public LongArray modMultiplyAlt(LongArray other, int m, int[] ks)
+    {
+        /*
+         * Find out the degree of each argument and handle the zero cases
+         */
+        int aDeg = degree();
+        if (aDeg == 0)
+        {
+            return this;
+        }
+        int bDeg = other.degree();
+        if (bDeg == 0)
+        {
+            return other;
+        }
+
+        /*
+         * Swap if necessary so that A is the smaller argument
+         */
+        LongArray A = this, B = other;
+        if (aDeg > bDeg)
+        {
+            A = other; B = this;
+            int tmp = aDeg; aDeg = bDeg; bDeg = tmp;
+        }
+
+        /*
+         * Establish the word lengths of the arguments and result
+         */
+        int aLen = (aDeg + 63) >>> 6;
+        int bLen = (bDeg + 63) >>> 6;
+        int cLen = (aDeg + bDeg + 62) >>> 6;
+
+        if (aLen == 1)
+        {
+            long a = A.m_ints[0];
+            if (a == 1L)
+            {
+                return B;
+            }
+
+            /*
+             * Fast path for small A, with performance dependent only on the number of set bits
+             */
+            long[] c = new long[cLen];
+            multiplyWord(a, B.m_ints, bLen, c, 0);
+
+            /*
+             * Reduce the raw answer against the reduction coefficients
+             */
+            return reduceResult(c, 0, cLen, m, ks);
+        }
+
+        // NOTE: This works, but is slower than width 4 processing
+//        if (aLen == 2)
+//        {
+//            /*
+//             * Use common-multiplicand optimization to save ~1/4 of the adds
+//             */
+//            long a1 = A.m_ints[0], a2 = A.m_ints[1];
+//            long aa = a1 & a2; a1 ^= aa; a2 ^= aa;
+//
+//            long[] b = B.m_ints;
+//            long[] c = new long[cLen];
+//            multiplyWord(aa, b, bLen, c, 1);
+//            add(c, 0, c, 1, cLen - 1);
+//            multiplyWord(a1, b, bLen, c, 0);
+//            multiplyWord(a2, b, bLen, c, 1);
+//
+//            /*
+//             * Reduce the raw answer against the reduction coefficients
+//             */
+//            return reduceResult(c, 0, cLen, m, ks);
+//        }
+
+        /*
+         * Determine the parameters of the interleaved window algorithm: the 'width' in bits to
+         * process together, the number of evaluation 'positions' implied by that width, and the
+         * 'top' position at which the regular window algorithm stops.
+         */
+        int width, positions, top, banks;
+
+        // NOTE: width 4 is the fastest over the entire range of sizes used in current crypto 
+//        width = 1; positions = 64; top = 64; banks = 4;
+//        width = 2; positions = 32; top = 64; banks = 4;
+//        width = 3; positions = 21; top = 63; banks = 3;
+        width = 4; positions = 16; top = 64; banks = 8;
+//        width = 5; positions = 13; top = 65; banks = 7;
+//        width = 7; positions = 9; top = 63; banks = 9;
+//        width = 8; positions = 8; top = 64; banks = 8;
+
+        /*
+         * Determine if B will get bigger during shifting
+         */
+        int shifts = top < 64 ? positions : positions - 1;
+        int bMax = (bDeg + shifts + 63) >>> 6;
+
+        int bTotal = bMax * banks, stride = width * banks;
+
+        /*
+         * Create a single temporary buffer, with an offset table to find the positions of things in it 
+         */
+        int[] ci = new int[1 << width];
+        int cTotal = aLen;
+        {
+            ci[0] = cTotal;
+            cTotal += bTotal;
+            ci[1] = cTotal;
+            for (int i = 2; i < ci.length; ++i)
+            {
+                cTotal += cLen;
+                ci[i] = cTotal;
+            }
+            cTotal += cLen;
+        }
+        // NOTE: Provide a safe dump for "high zeroes" since we are adding 'bMax' and not 'bLen'
+        ++cTotal;
+
+        long[] c = new long[cTotal];
+
+        // Prepare A in interleaved form, according to the chosen width
+        interleave(A.m_ints, 0, c, 0, aLen, width);
+
+        // Make a working copy of B, since we will be shifting it
+        {
+            int bOff = aLen;
+            System.arraycopy(B.m_ints, 0, c, bOff, bLen);
+            for (int bank = 1; bank < banks; ++bank)
+            {
+                shiftUp(c, aLen, c, bOff += bMax, bMax, bank);
+            }
+        }
+
+        /*
+         * The main loop analyzes the interleaved windows in A, and for each non-zero window
+         * a single word-array XOR is performed to a carefully selected slice of 'c'. The loop is
+         * breadth-first, checking the lowest window in each word, then looping again for the
+         * next higher window position.
+         */
+        int MASK = (1 << width) - 1;
+
+        int k = 0;
+        for (;;)
+        {
+            int aPos = 0;
+            do
+            {
+                long aVal = c[aPos] >>> k;
+                int bank = 0, bOff = aLen;
+                for (;;)
+                {
+                    int index = (int)(aVal) & MASK;
+                    if (index != 0)
+                    {
+                        /*
+                         * Add to a 'c' buffer based on the bit-pattern of 'index'. Since A is in
+                         * interleaved form, the bits represent the current B shifted by 0, 'positions',
+                         * 'positions' * 2, ..., 'positions' * ('width' - 1)
+                         */
+                        add(c, aPos + ci[index], c, bOff, bMax);
+                    }
+                    if (++bank == banks)
+                    {
+                        break;
+                    }
+                    bOff += bMax;
+                    aVal >>>= width;
+                }
+            }
+            while (++aPos < aLen);
+
+            if ((k += stride) >= top)
+            {
+                if (k >= 64)
+                {
+                    break;
+                }
+
+                /*
+                 * Adjustment for window setups with top == 63, the final bit (if any) is processed
+                 * as the top-bit of a window
+                 */
+                k = 64 - width;
+                MASK &= MASK << (top - k);
+            }
+
+            /*
+             * After each position has been checked for all words of A, B is shifted up 1 place
+             */
+            shiftUp(c, aLen, bTotal, banks);
+        }
+
+        int ciPos = ci.length;
+        while (--ciPos > 1)
+        {
+            if ((ciPos & 1L) == 0L)
+            {
+                /*
+                 * For even numbers, shift contents and add to the half-position
+                 */
+                addShiftedUp(c, ci[ciPos >>> 1], c, ci[ciPos], cLen, positions);
+            }
+            else
+            {
+                /*
+                 * For odd numbers, 'distribute' contents to the result and the next-lowest position
+                 */
+                distribute(c, ci[ciPos], ci[ciPos - 1], ci[1], cLen);
+            }
+        }
+
+        /*
+         * Finally the raw answer is collected, reduce it against the reduction coefficients
+         */
+        return reduceResult(c, ci[1], cLen, m, ks);
+    }
+
+    private static LongArray reduceResult(long[] buf, int off, int len, int m, int[] ks)
+    {
+        int rLen = reduceInPlace(buf, off, len, m, ks);
+        return new LongArray(buf, off, rLen);
+    }
+
+//    private static void deInterleave(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+//    {
+//        for (int i = 0; i < count; ++i)
+//        {
+//            z[zOff + i] = deInterleave(x[zOff + i], rounds);
+//        }
+//    }
+//
+//    private static long deInterleave(long x, int rounds)
+//    {
+//        while (--rounds >= 0)
+//        {
+//            x = deInterleave32(x & DEINTERLEAVE_MASK) | (deInterleave32((x >>> 1) & DEINTERLEAVE_MASK) << 32);
+//        }
+//        return x;
+//    }
+//
+//    private static long deInterleave32(long x)
+//    {
+//        x = (x | (x >>> 1)) & 0x3333333333333333L;
+//        x = (x | (x >>> 2)) & 0x0F0F0F0F0F0F0F0FL;
+//        x = (x | (x >>> 4)) & 0x00FF00FF00FF00FFL;
+//        x = (x | (x >>> 8)) & 0x0000FFFF0000FFFFL;
+//        x = (x | (x >>> 16)) & 0x00000000FFFFFFFFL;
+//        return x;
+//    }
+
+    private static int reduceInPlace(long[] buf, int off, int len, int m, int[] ks)
+    {
+        int mLen = (m + 63) >>> 6;
+        if (len < mLen)
+        {
+            return len;
+        }
+
+        int numBits = Math.min(len << 6, (m << 1) - 1); // TODO use actual degree?
+        int excessBits = (len << 6) - numBits;
+        while (excessBits >= 64)
+        {
+            --len;
+            excessBits -= 64;
+        }
+
+        int kLen = ks.length, kMax = ks[kLen - 1], kNext = kLen > 1 ? ks[kLen - 2] : 0;
+        int wordWiseLimit = Math.max(m, kMax + 64);
+        int vectorableWords = (excessBits + Math.min(numBits - wordWiseLimit, m - kNext)) >> 6;
+        if (vectorableWords > 1)
+        {
+            int vectorWiseWords = len - vectorableWords;
+            reduceVectorWise(buf, off, len, vectorWiseWords, m, ks);
+            while (len > vectorWiseWords)
+            {
+                buf[off + --len] = 0L;
+            }
+            numBits = vectorWiseWords << 6;
+        }
+
+        if (numBits > wordWiseLimit)
+        {
+            reduceWordWise(buf, off, len, wordWiseLimit, m, ks);
+            numBits = wordWiseLimit;
+        }
+
+        if (numBits > m)
+        {
+            reduceBitWise(buf, off, numBits, m, ks);
+        }
+
+        return mLen;
+    }
+
+    private static void reduceBitWise(long[] buf, int off, int bitlength, int m, int[] ks)
+    {
+        while (--bitlength >= m)
+        {
+            if (testBit(buf, off, bitlength))
+            {
+                reduceBit(buf, off, bitlength, m, ks);
+            }
+        }
+    }
+
+    private static void reduceBit(long[] buf, int off, int bit, int m, int[] ks)
+    {
+        flipBit(buf, off, bit);
+        int base = bit - m;
+        int j = ks.length;
+        while (--j >= 0)
+        {
+            flipBit(buf, off, ks[j] + base);
+        }
+        flipBit(buf, off, base);
+    }
+
+    private static void reduceWordWise(long[] buf, int off, int len, int toBit, int m, int[] ks)
+    {
+        int toPos = toBit >>> 6;
+
+        while (--len > toPos)
+        {
+            long word = buf[off + len];
+            if (word != 0)
+            {
+                buf[off + len] = 0;
+                reduceWord(buf, off, (len << 6), word, m, ks);
+            }
+        }
+
+        int partial = toBit & 0x3F;
+        long word = buf[off + toPos] >>> partial;
+        if (word != 0)
+        {
+            buf[off + toPos] ^= word << partial;
+            reduceWord(buf, off, toBit, word, m, ks);
+        }
+    }
+
+    private static void reduceWord(long[] buf, int off, int bit, long word, int m, int[] ks)
+    {
+        int offset = bit - m;
+        int j = ks.length;
+        while (--j >= 0)
+        {
+            flipWord(buf, off, offset + ks[j], word);
+        }
+        flipWord(buf, off, offset, word);
+    }
+
+    private static void reduceVectorWise(long[] buf, int off, int len, int words, int m, int[] ks)
+    {
+        /*
+         * NOTE: It's important we go from highest coefficient to lowest, because for the highest
+         * one (only) we allow the ranges to partially overlap, and therefore any changes must take
+         * effect for the subsequent lower coefficients.
+         */
+        int baseBit = (words << 6) - m;
+        int j = ks.length;
+        while (--j >= 0)
+        {
+            flipVector(buf, off, buf, off + words, len - words, baseBit + ks[j]);
+        }
+        flipVector(buf, off, buf, off + words, len - words, baseBit);
+    }
+
+    private static void flipVector(long[] x, int xOff, long[] y, int yOff, int yLen, int bits)
+    {
+        xOff += bits >>> 6;
+        bits &= 0x3F;
+
+        if (bits == 0)
+        {
+            add(x, xOff, y, yOff, yLen);
+        }
+        else
+        {
+            long carry = addShiftedDown(x, xOff + 1, y, yOff, yLen, 64 - bits);
+            x[xOff] ^= carry;
+        }
+    }
+
+    public LongArray modSquare(int m, int[] ks)
+    {
+        int len = getUsedLength();
+        if (len == 0)
+        {
+            return this;
+        }
+
+        int _2len = len << 1;
+        long[] r = new long[_2len];
+
+        int pos = 0;
+        while (pos < _2len)
+        {
+            long mi = m_ints[pos >>> 1];
+            r[pos++] = interleave2_32to64((int)mi);
+            r[pos++] = interleave2_32to64((int)(mi >>> 32));
+        }
+
+        return new LongArray(r, 0, reduceInPlace(r, 0, r.length, m, ks));
+    }
+
+//    private LongArray modSquareN(int n, int m, int[] ks)
+//    {
+//        int len = getUsedLength();
+//        if (len == 0)
+//        {
+//            return this;
+//        }
+//
+//        int mLen = (m + 63) >>> 6;
+//        long[] r = new long[mLen << 1];
+//        System.arraycopy(m_ints, 0, r, 0, len);
+//
+//        while (--n >= 0)
+//        {
+//            squareInPlace(r, len, m, ks);
+//            len = reduceInPlace(r, 0, r.length, m, ks);
+//        }
+//
+//        return new LongArray(r, 0, len);
+//    }
+//
+//    private static void squareInPlace(long[] x, int xLen, int m, int[] ks)
+//    {
+//        int pos = xLen << 1;
+//        while (--xLen >= 0)
+//        {
+//            long xVal = x[xLen];
+//            x[--pos] = interleave2_32to64((int)(xVal >>> 32));
+//            x[--pos] = interleave2_32to64((int)xVal);
+//        }
+//    }
+
+    private static void interleave(long[] x, int xOff, long[] z, int zOff, int count, int width)
+    {
+        switch (width)
+        {
+        case 3:
+            interleave3(x, xOff, z, zOff, count);
+            break;
+        case 5:
+            interleave5(x, xOff, z, zOff, count);
+            break;
+        case 7:
+            interleave7(x, xOff, z, zOff, count);
+            break;
+        default:
+            interleave2_n(x, xOff, z, zOff, count, bitLengths[width] - 1);
+            break;
+        }
+    }
+
+    private static void interleave3(long[] x, int xOff, long[] z, int zOff, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            z[zOff + i] = interleave3(x[xOff + i]);
+        }
+    }
+
+    private static long interleave3(long x)
+    {
+        long z = x & (1L << 63);
+        return z
+            | interleave3_21to63((int)x & 0x1FFFFF)
+            | interleave3_21to63((int)(x >>> 21) & 0x1FFFFF) << 1
+            | interleave3_21to63((int)(x >>> 42) & 0x1FFFFF) << 2;
+
+//        int zPos = 0, wPos = 0, xPos = 0;
+//        for (;;)
+//        {
+//            z |= ((x >>> xPos) & 1L) << zPos;
+//            if (++zPos == 63)
+//            {
+//                String sz2 = Long.toBinaryString(z);
+//                return z;
+//            }
+//            if ((xPos += 21) >= 63)
+//            {
+//                xPos = ++wPos;
+//            }
+//        }
+    }
+
+    private static long interleave3_21to63(int x)
+    {
+        int r00 = INTERLEAVE3_TABLE[x & 0x7F];
+        int r21 = INTERLEAVE3_TABLE[(x >>> 7) & 0x7F];
+        int r42 = INTERLEAVE3_TABLE[x >>> 14];
+        return (r42 & 0xFFFFFFFFL) << 42 | (r21 & 0xFFFFFFFFL) << 21 | (r00 & 0xFFFFFFFFL);
+    }
+
+    private static void interleave5(long[] x, int xOff, long[] z, int zOff, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            z[zOff + i] = interleave5(x[xOff + i]);
+        }
+    }
+
+    private static long interleave5(long x)
+    {
+        return interleave3_13to65((int)x & 0x1FFF)
+            | interleave3_13to65((int)(x >>> 13) & 0x1FFF) << 1
+            | interleave3_13to65((int)(x >>> 26) & 0x1FFF) << 2
+            | interleave3_13to65((int)(x >>> 39) & 0x1FFF) << 3
+            | interleave3_13to65((int)(x >>> 52) & 0x1FFF) << 4;
+
+//        long z = 0;
+//        int zPos = 0, wPos = 0, xPos = 0;
+//        for (;;)
+//        {
+//            z |= ((x >>> xPos) & 1L) << zPos;
+//            if (++zPos == 64)
+//            {
+//                return z;
+//            }
+//            if ((xPos += 13) >= 64)
+//            {
+//                xPos = ++wPos;
+//            }
+//        }
+    }
+
+    private static long interleave3_13to65(int x)
+    {
+        int r00 = INTERLEAVE5_TABLE[x & 0x7F];
+        int r35 = INTERLEAVE5_TABLE[x >>> 7];
+        return (r35 & 0xFFFFFFFFL) << 35 | (r00 & 0xFFFFFFFFL);
+    }
+
+    private static void interleave7(long[] x, int xOff, long[] z, int zOff, int count)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            z[zOff + i] = interleave7(x[xOff + i]);
+        }
+    }
+
+    private static long interleave7(long x)
+    {
+        long z = x & (1L << 63);
+        return z
+            | INTERLEAVE7_TABLE[(int)x & 0x1FF]
+            | INTERLEAVE7_TABLE[(int)(x >>> 9) & 0x1FF] << 1
+            | INTERLEAVE7_TABLE[(int)(x >>> 18) & 0x1FF] << 2
+            | INTERLEAVE7_TABLE[(int)(x >>> 27) & 0x1FF] << 3
+            | INTERLEAVE7_TABLE[(int)(x >>> 36) & 0x1FF] << 4
+            | INTERLEAVE7_TABLE[(int)(x >>> 45) & 0x1FF] << 5
+            | INTERLEAVE7_TABLE[(int)(x >>> 54) & 0x1FF] << 6;
+
+//        int zPos = 0, wPos = 0, xPos = 0;
+//        for (;;)
+//        {
+//            z |= ((x >>> xPos) & 1L) << zPos;
+//            if (++zPos == 63)
+//            {
+//                return z;
+//            }
+//            if ((xPos += 9) >= 63)
+//            {
+//                xPos = ++wPos;
+//            }
+//        }
+    }
+
+    private static void interleave2_n(long[] x, int xOff, long[] z, int zOff, int count, int rounds)
+    {
+        for (int i = 0; i < count; ++i)
+        {
+            z[zOff + i] = interleave2_n(x[xOff + i], rounds);
+        }
+    }
+
+    private static long interleave2_n(long x, int rounds)
+    {
+        while (rounds > 1)
+        {
+            rounds -= 2;
+            x = interleave4_16to64((int)x & 0xFFFF)
+                | interleave4_16to64((int)(x >>> 16) & 0xFFFF) << 1
+                | interleave4_16to64((int)(x >>> 32) & 0xFFFF) << 2
+                | interleave4_16to64((int)(x >>> 48) & 0xFFFF) << 3;
+        }
+        if (rounds > 0)
+        {
+            x = interleave2_32to64((int)x) | interleave2_32to64((int)(x >>> 32)) << 1;
+        }
+        return x;
+    }
+
+    private static long interleave4_16to64(int x)
+    {
+        int r00 = INTERLEAVE4_TABLE[x & 0xFF];
+        int r32 = INTERLEAVE4_TABLE[x >>> 8];
+        return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+    }
+
+    private static long interleave2_32to64(int x)
+    {
+        int r00 = INTERLEAVE2_TABLE[x & 0xFF] | INTERLEAVE2_TABLE[(x >>> 8) & 0xFF] << 16;
+        int r32 = INTERLEAVE2_TABLE[(x >>> 16) & 0xFF] | INTERLEAVE2_TABLE[x >>> 24] << 16;
+        return (r32 & 0xFFFFFFFFL) << 32 | (r00 & 0xFFFFFFFFL);
+    }
+
+//    private static LongArray expItohTsujii2(LongArray B, int n, int m, int[] ks)
+//    {
+//        LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+//        int scale = 1;
+//
+//        int numTerms = n;
+//        while (numTerms > 1)
+//        {
+//            if ((numTerms & 1) != 0)
+//            {
+//                t3 = t3.modMultiply(t1, m, ks);
+//                t1 = t1.modSquareN(scale, m, ks);
+//            }
+//
+//            LongArray t2 = t1.modSquareN(scale, m, ks);
+//            t1 = t1.modMultiply(t2, m, ks);
+//            numTerms >>>= 1; scale <<= 1;
+//        }
+//
+//        return t3.modMultiply(t1, m, ks);
+//    }
+//
+//    private static LongArray expItohTsujii23(LongArray B, int n, int m, int[] ks)
+//    {
+//        LongArray t1 = B, t3 = new LongArray(new long[]{ 1L });
+//        int scale = 1;
+//
+//        int numTerms = n;
+//        while (numTerms > 1)
+//        {
+//            boolean m03 = numTerms % 3 == 0;
+//            boolean m14 = !m03 && (numTerms & 1) != 0;
+//
+//            if (m14)
+//            {
+//                t3 = t3.modMultiply(t1, m, ks);
+//                t1 = t1.modSquareN(scale, m, ks);
+//            }
+//
+//            LongArray t2 = t1.modSquareN(scale, m, ks);
+//            t1 = t1.modMultiply(t2, m, ks);
+//
+//            if (m03)
+//            {
+//                t2 = t2.modSquareN(scale, m, ks);
+//                t1 = t1.modMultiply(t2, m, ks);
+//                numTerms /= 3; scale *= 3;
+//            }
+//            else
+//            {
+//                numTerms >>>= 1; scale <<= 1;
+//            }
+//        }
+//
+//        return t3.modMultiply(t1, m, ks);
+//    }
+//
+//    private static LongArray expItohTsujii235(LongArray B, int n, int m, int[] ks)
+//    {
+//        LongArray t1 = B, t4 = new LongArray(new long[]{ 1L });
+//        int scale = 1;
+//
+//        int numTerms = n;
+//        while (numTerms > 1)
+//        {
+//            if (numTerms % 5 == 0)
+//            {
+////                t1 = expItohTsujii23(t1, 5, m, ks);
+//
+//                LongArray t3 = t1;
+//                t1 = t1.modSquareN(scale, m, ks);
+//
+//                LongArray t2 = t1.modSquareN(scale, m, ks);
+//                t1 = t1.modMultiply(t2, m, ks);
+//                t2 = t1.modSquareN(scale << 1, m, ks);
+//                t1 = t1.modMultiply(t2, m, ks);
+//
+//                t1 = t1.modMultiply(t3, m, ks);
+//
+//                numTerms /= 5; scale *= 5;
+//                continue;
+//            }
+//
+//            boolean m03 = numTerms % 3 == 0;
+//            boolean m14 = !m03 && (numTerms & 1) != 0;
+//
+//            if (m14)
+//            {
+//                t4 = t4.modMultiply(t1, m, ks);
+//                t1 = t1.modSquareN(scale, m, ks);
+//            }
+//
+//            LongArray t2 = t1.modSquareN(scale, m, ks);
+//            t1 = t1.modMultiply(t2, m, ks);
+//
+//            if (m03)
+//            {
+//                t2 = t2.modSquareN(scale, m, ks);
+//                t1 = t1.modMultiply(t2, m, ks);
+//                numTerms /= 3; scale *= 3;
+//            }
+//            else
+//            {
+//                numTerms >>>= 1; scale <<= 1;
+//            }
+//        }
+//
+//        return t4.modMultiply(t1, m, ks);
+//    }
+
+    public LongArray modInverse(int m, int[] ks)
+    {
+        /*
+         * Fermat's Little Theorem
+         */
+//        LongArray A = this;
+//        LongArray B = A.modSquare(m, ks);
+//        LongArray R0 = B, R1 = B;
+//        for (int i = 2; i < m; ++i)
+//        {
+//            R1 = R1.modSquare(m, ks);
+//            R0 = R0.modMultiply(R1, m, ks);
+//        }
+//
+//        return R0;
+
+        /*
+         * Itoh-Tsujii
+         */
+//        LongArray B = modSquare(m, ks);
+//        switch (m)
+//        {
+//        case 409:
+//            return expItohTsujii23(B, m - 1, m, ks);
+//        case 571:
+//            return expItohTsujii235(B, m - 1, m, ks);
+//        case 163:
+//        case 233:
+//        case 283:
+//        default:
+//            return expItohTsujii2(B, m - 1, m, ks);
+//        }
+
+        /*
+         * Inversion in F2m using the extended Euclidean algorithm
+         * 
+         * Input: A nonzero polynomial a(z) of degree at most m-1
+         * Output: a(z)^(-1) mod f(z)
+         */
+        int uzDegree = degree();
+        if (uzDegree == 1)
+        {
+            return this;
+        }
+
+        // u(z) := a(z)
+        LongArray uz = (LongArray)clone();
+
+        int t = (m + 63) >>> 6;
+
+        // v(z) := f(z)
+        LongArray vz = new LongArray(t);
+        reduceBit(vz.m_ints, 0, m, m, ks);
+
+        // g1(z) := 1, g2(z) := 0
+        LongArray g1z = new LongArray(t);
+        g1z.m_ints[0] = 1L;
+        LongArray g2z = new LongArray(t);
+
+        int[] uvDeg = new int[]{ uzDegree, m + 1 };
+        LongArray[] uv = new LongArray[]{ uz, vz };
+
+        int[] ggDeg = new int[]{ 1, 0 };
+        LongArray[] gg = new LongArray[]{ g1z, g2z };
+
+        int b = 1;
+        int duv1 = uvDeg[b];
+        int dgg1 = ggDeg[b];
+        int j = duv1 - uvDeg[1 - b];
+
+        for (;;)
+        {
+            if (j < 0)
+            {
+                j = -j;
+                uvDeg[b] = duv1;
+                ggDeg[b] = dgg1;
+                b = 1 - b;
+                duv1 = uvDeg[b];
+                dgg1 = ggDeg[b];
+            }
+
+            uv[b].addShiftedByBitsSafe(uv[1 - b], uvDeg[1 - b], j);
+
+            int duv2 = uv[b].degreeFrom(duv1);
+            if (duv2 == 0)
+            {
+                return gg[1 - b];
+            }
+
+            {
+                int dgg2 = ggDeg[1 - b];
+                gg[b].addShiftedByBitsSafe(gg[1 - b], dgg2, j);
+                dgg2 += j;
+
+                if (dgg2 > dgg1)
+                {
+                    dgg1 = dgg2;
+                }
+                else if (dgg2 == dgg1)
+                {
+                    dgg1 = gg[b].degreeFrom(dgg1);
+                }
+            }
+
+            j += (duv2 - duv1);
+            duv1 = duv2;
+        }
+    }
+
+    public boolean equals(Object o)
+    {
+        if (!(o instanceof LongArray))
+        {
+            return false;
+        }
+        LongArray other = (LongArray) o;
+        int usedLen = getUsedLength();
+        if (other.getUsedLength() != usedLen)
+        {
+            return false;
+        }
+        for (int i = 0; i < usedLen; i++)
+        {
+            if (m_ints[i] != other.m_ints[i])
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public int hashCode()
+    {
+        int usedLen = getUsedLength();
+        int hash = 1;
+        for (int i = 0; i < usedLen; i++)
+        {
+            long mi = m_ints[i];
+            hash *= 31;
+            hash ^= (int)mi;
+            hash *= 31;
+            hash ^= (int)(mi >>> 32);
+        }
+        return hash;
+    }
+
+    public Object clone()
+    {
+        return new LongArray(Arrays.clone(m_ints));
+    }
+
+    public String toString()
+    {
+        int i = getUsedLength();
+        if (i == 0)
+        {
+            return "0";
+        }
+
+        StringBuffer sb = new StringBuffer(Long.toBinaryString(m_ints[--i]));
+        while (--i >= 0)
+        {
+            String s = Long.toBinaryString(m_ints[i]);
+
+            // Add leading zeroes, except for highest significant word
+            int len = s.length();
+            if (len < 64)
+            {
+                sb.append(ZEROES.substring(len));
+            }
+
+            sb.append(s);
+        }
+        return sb.toString();
+    }
+}
\ No newline at end of file
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/MixedNafR2LMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/MixedNafR2LMultiplier.java
new file mode 100644
index 0000000..6d5fe92
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/MixedNafR2LMultiplier.java
@@ -0,0 +1,77 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left) using
+ * mixed coordinates.
+ */
+public class MixedNafR2LMultiplier extends AbstractECMultiplier
+{
+    protected int additionCoord, doublingCoord;
+
+    /**
+     * By default, addition will be done in Jacobian coordinates, and doubling will be done in
+     * Modified Jacobian coordinates (independent of the original coordinate system of each point).
+     */
+    public MixedNafR2LMultiplier()
+    {
+        this(ECCurve.COORD_JACOBIAN, ECCurve.COORD_JACOBIAN_MODIFIED);
+    }
+
+    public MixedNafR2LMultiplier(int additionCoord, int doublingCoord)
+    {
+        this.additionCoord = additionCoord;
+        this.doublingCoord = doublingCoord;
+    }
+
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        ECCurve curveOrig = p.getCurve();
+
+        ECCurve curveAdd = configureCurve(curveOrig, additionCoord);
+        ECCurve curveDouble = configureCurve(curveOrig, doublingCoord);
+
+        int[] naf = WNafUtil.generateCompactNaf(k);
+
+        ECPoint Ra = curveAdd.getInfinity();
+        ECPoint Td = curveDouble.importPoint(p);
+
+        int zeroes = 0;
+        for (int i = 0; i < naf.length; ++i)
+        {
+            int ni = naf[i];
+            int digit = ni >> 16;
+            zeroes += ni & 0xFFFF;
+
+            Td = Td.timesPow2(zeroes);
+
+            ECPoint Tj = curveAdd.importPoint(Td);
+            if (digit < 0)
+            {
+                Tj = Tj.negate();
+            }
+
+            Ra = Ra.add(Tj);
+
+            zeroes = 1;
+        }
+
+        return curveOrig.importPoint(Ra);
+    }
+
+    protected ECCurve configureCurve(ECCurve c, int coord)
+    {
+        if (c.getCoordinateSystem() == coord)
+        {
+            return c;
+        }
+
+        if (!c.supportsCoordinateSystem(coord))
+        {
+            throw new IllegalArgumentException("Coordinate system " + coord + " not supported by this curve");
+        }
+
+        return c.configure().setCoordinateSystem(coord).create();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/MontgomeryLadderMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/MontgomeryLadderMultiplier.java
new file mode 100644
index 0000000..cd969b5
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/MontgomeryLadderMultiplier.java
@@ -0,0 +1,25 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class MontgomeryLadderMultiplier extends AbstractECMultiplier
+{
+    /**
+     * Montgomery ladder.
+     */
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        ECPoint[] R = new ECPoint[]{ p.getCurve().getInfinity(), p };
+
+        int n = k.bitLength();
+        int i = n;
+        while (--i >= 0)
+        {
+            int b = k.testBit(i) ? 1 : 0;
+            int bp = 1 - b;
+            R[bp] = R[bp].add(R[b]);
+            R[b] = R[b].twice();
+        }
+        return R[0];
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/NafL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/NafL2RMultiplier.java
new file mode 100644
index 0000000..91d91d1
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/NafL2RMultiplier.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (left-to-right).
+ */
+public class NafL2RMultiplier extends AbstractECMultiplier
+{
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        int[] naf = WNafUtil.generateCompactNaf(k);
+
+        ECPoint addP = p.normalize(), subP = addP.negate();
+
+        ECPoint R = p.getCurve().getInfinity();
+
+        int i = naf.length;
+        while (--i >= 0)
+        {
+            int ni = naf[i];
+            int digit = ni >> 16, zeroes = ni & 0xFFFF;
+
+            R = R.twicePlus(digit < 0 ? subP : addP);
+            R = R.timesPow2(zeroes);
+        }
+
+        return R;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/NafR2LMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/NafR2LMultiplier.java
new file mode 100644
index 0000000..aed2336
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/NafR2LMultiplier.java
@@ -0,0 +1,31 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the NAF (Non-Adjacent Form) multiplication algorithm (right-to-left).
+ */
+public class NafR2LMultiplier extends AbstractECMultiplier
+{
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        int[] naf = WNafUtil.generateCompactNaf(k);
+
+        ECPoint R0 = p.getCurve().getInfinity(), R1 = p;
+
+        int zeroes = 0;
+        for (int i = 0; i < naf.length; ++i)
+        {
+            int ni = naf[i];
+            int digit = ni >> 16;
+            zeroes += ni & 0xFFFF;
+
+            R1 = R1.timesPow2(zeroes);
+            R0 = R0.add(digit < 0 ? R1.negate() : R1);
+
+            zeroes = 1;
+        }
+
+        return R0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java
index 804dcf7..3849858 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/PreCompInfo.java
@@ -2,9 +2,9 @@
 
 /**
  * Interface for classes storing precomputation data for multiplication
- * algorithms. Used as a Memento (see GOF patterns) for
- * <code>WNafMultiplier</code>.
+ * algorithms. Used as a Memento (see GOF patterns) by e.g. 
+ * <code>WNafL2RMultiplier</code>.
  */
-interface PreCompInfo
+public interface PreCompInfo
 {
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java
index c1dd548..3601856 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ReferenceMultiplier.java
@@ -2,7 +2,7 @@
 
 import java.math.BigInteger;
 
-class ReferenceMultiplier implements ECMultiplier
+public class ReferenceMultiplier extends AbstractECMultiplier
 {
     /**
      * Simple shift-and-add multiplication. Serves as reference implementation
@@ -13,17 +13,24 @@
      * @param k The factor by which to multiply.
      * @return The result of the point multiplication <code>k * p</code>.
      */
-    public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
     {
         ECPoint q = p.getCurve().getInfinity();
         int t = k.bitLength();
-        for (int i = 0; i < t; i++)
+        if (t > 0)
         {
-            if (k.testBit(i))
+            if (k.testBit(0))
             {
-                q = q.add(p);
+                q = p;
             }
-            p = p.twice();
+            for (int i = 1; i < t; i++)
+            {
+                p = p.twice();
+                if (k.testBit(i))
+                {
+                    q = q.add(p);
+                }
+            }
         }
         return q;
     }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java
index af4355f..42d6738 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/Tnaf.java
@@ -392,15 +392,7 @@
      */
     public static ECPoint.F2m tau(ECPoint.F2m p)
     {
-        if (p.isInfinity())
-        {
-            return p;
-        }
-
-        ECFieldElement x = p.getX();
-        ECFieldElement y = p.getY();
-
-        return new ECPoint.F2m(p.getCurve(), x.square(), y.square(), p.isCompressed());
+        return p.tau();
     }
 
     /**
@@ -415,23 +407,17 @@
      */
     public static byte getMu(ECCurve.F2m curve)
     {
-        BigInteger a = curve.getA().toBigInteger();
-        byte mu;
+        if (!curve.isKoblitz())
+        {
+            throw new IllegalArgumentException("No Koblitz curve (ABC), TNAF multiplication not possible");
+        }
 
-        if (a.equals(ECConstants.ZERO))
+        if (curve.getA().isZero())
         {
-            mu = -1;
+            return -1;
         }
-        else if (a.equals(ECConstants.ONE))
-        {
-            mu = 1;
-        }
-        else
-        {
-            throw new IllegalArgumentException("No Koblitz curve (ABC), " +
-                    "TNAF multiplication not possible");
-        }
-        return mu;
+
+        return 1;
     }
 
     /**
@@ -838,7 +824,9 @@
         {
             pu[i] = Tnaf.multiplyFromTnaf(p, alphaTnaf[i]);
         }
-        
+
+        p.getCurve().normalizeAll(pu);
+
         return pu;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java
new file mode 100644
index 0000000..59a9313
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafL2RMultiplier.java
@@ -0,0 +1,101 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+/**
+ * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
+ * algorithm.
+ */
+public class WNafL2RMultiplier extends AbstractECMultiplier
+{
+    /**
+     * Multiplies <code>this</code> by an integer <code>k</code> using the
+     * Window NAF method.
+     * @param k The integer by which <code>this</code> is multiplied.
+     * @return A new <code>ECPoint</code> which equals <code>this</code>
+     * multiplied by <code>k</code>.
+     */
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        // Clamp the window width in the range [2, 16]
+        int width = Math.max(2, Math.min(16, getWindowSize(k.bitLength())));
+
+        WNafPreCompInfo wnafPreCompInfo = WNafUtil.precompute(p, width, true);
+        ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+        ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
+
+        int[] wnaf = WNafUtil.generateCompactWindowNaf(width, k);
+
+        ECPoint R = p.getCurve().getInfinity();
+
+        int i = wnaf.length;
+
+        /*
+         * NOTE This code optimizes the first window using the precomputed points to substitute an
+         * addition for 2 or more doublings.
+         */
+        if (i > 1)
+        {
+            int wi = wnaf[--i];
+            int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+            int n = Math.abs(digit);
+            ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+
+            /*
+             * NOTE: We use this optimization conservatively, since some coordinate systems have
+             * significantly cheaper doubling relative to addition.
+             * 
+             * (n << 2) selects precomputed values in the lower half of the table
+             * (n << 3) selects precomputed values in the lower quarter of the table
+             */
+            //if ((n << 2) < (1 << width))
+            if ((n << 3) < (1 << width))
+            {
+                int highest = LongArray.bitLengths[n];
+                int lowBits =  n ^ (1 << (highest - 1));
+                int scale = width - highest;
+
+                int i1 = ((1 << (width - 1)) - 1);
+                int i2 = (lowBits << scale) + 1;
+                R = table[i1 >>> 1].add(table[i2 >>> 1]);
+
+                zeroes -= scale;
+
+//              System.out.println("Optimized: 2^" + scale + " * " + n + " = " + i1 + " + " + i2);
+            }
+            else
+            {
+                R = table[n >>> 1];
+            }
+
+            R = R.timesPow2(zeroes);
+        }
+
+        while (i > 0)
+        {
+            int wi = wnaf[--i];
+            int digit = wi >> 16, zeroes = wi & 0xFFFF;
+
+            int n = Math.abs(digit);
+            ECPoint[] table = digit < 0 ? preCompNeg : preComp;
+            ECPoint r = table[n >>> 1];
+
+            R = R.twicePlus(r);
+            R = R.timesPow2(zeroes);
+        }
+
+        return R;
+    }
+
+    /**
+     * Determine window width to use for a scalar multiplication of the given size.
+     * 
+     * @param bits the bit-length of the scalar to multiply by
+     * @return the window size to use
+     */
+    protected int getWindowSize(int bits)
+    {
+        return WNafUtil.getWindowSize(bits);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java
deleted file mode 100644
index 10c8ed2..0000000
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafMultiplier.java
+++ /dev/null
@@ -1,240 +0,0 @@
-package org.bouncycastle.math.ec;
-
-import java.math.BigInteger;
-
-/**
- * Class implementing the WNAF (Window Non-Adjacent Form) multiplication
- * algorithm.
- */
-class WNafMultiplier implements ECMultiplier
-{
-    /**
-     * Computes the Window NAF (non-adjacent Form) of an integer.
-     * @param width The width <code>w</code> of the Window NAF. The width is
-     * defined as the minimal number <code>w</code>, such that for any
-     * <code>w</code> consecutive digits in the resulting representation, at
-     * most one is non-zero.
-     * @param k The integer of which the Window NAF is computed.
-     * @return The Window NAF of the given width, such that the following holds:
-     * <code>k = &sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
-     * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
-     * returned <code>byte[]</code>.
-     */
-    public byte[] windowNaf(byte width, BigInteger k)
-    {
-        // The window NAF is at most 1 element longer than the binary
-        // representation of the integer k. byte can be used instead of short or
-        // int unless the window width is larger than 8. For larger width use
-        // short or int. However, a width of more than 8 is not efficient for
-        // m = log2(q) smaller than 2305 Bits. Note: Values for m larger than
-        // 1000 Bits are currently not used in practice.
-        byte[] wnaf = new byte[k.bitLength() + 1];
-
-        // 2^width as short and BigInteger
-        short pow2wB = (short)(1 << width);
-        BigInteger pow2wBI = BigInteger.valueOf(pow2wB);
-
-        int i = 0;
-
-        // The actual length of the WNAF
-        int length = 0;
-
-        // while k >= 1
-        while (k.signum() > 0)
-        {
-            // if k is odd
-            if (k.testBit(0))
-            {
-                // k mod 2^width
-                BigInteger remainder = k.mod(pow2wBI);
-
-                // if remainder > 2^(width - 1) - 1
-                if (remainder.testBit(width - 1))
-                {
-                    wnaf[i] = (byte)(remainder.intValue() - pow2wB);
-                }
-                else
-                {
-                    wnaf[i] = (byte)remainder.intValue();
-                }
-                // wnaf[i] is now in [-2^(width-1), 2^(width-1)-1]
-
-                k = k.subtract(BigInteger.valueOf(wnaf[i]));
-                length = i;
-            }
-            else
-            {
-                wnaf[i] = 0;
-            }
-
-            // k = k/2
-            k = k.shiftRight(1);
-            i++;
-        }
-
-        length++;
-
-        // Reduce the WNAF array to its actual length
-        byte[] wnafShort = new byte[length];
-        System.arraycopy(wnaf, 0, wnafShort, 0, length);
-        return wnafShort;
-    }
-
-    /**
-     * Multiplies <code>this</code> by an integer <code>k</code> using the
-     * Window NAF method.
-     * @param k The integer by which <code>this</code> is multiplied.
-     * @return A new <code>ECPoint</code> which equals <code>this</code>
-     * multiplied by <code>k</code>.
-     */
-    public ECPoint multiply(ECPoint p, BigInteger k, PreCompInfo preCompInfo)
-    {
-        WNafPreCompInfo wnafPreCompInfo;
-
-        if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo))
-        {
-            wnafPreCompInfo = (WNafPreCompInfo)preCompInfo;
-        }
-        else
-        {
-            // Ignore empty PreCompInfo or PreCompInfo of incorrect type
-            wnafPreCompInfo = new WNafPreCompInfo();
-        }
-
-        // floor(log2(k))
-        int m = k.bitLength();
-
-        // width of the Window NAF
-        byte width;
-
-        // Required length of precomputation array
-        int reqPreCompLen;
-
-        // Determine optimal width and corresponding length of precomputation
-        // array based on literature values
-        if (m < 13)
-        {
-            width = 2;
-            reqPreCompLen = 1;
-        }
-        else
-        {
-            if (m < 41)
-            {
-                width = 3;
-                reqPreCompLen = 2;
-            }
-            else
-            {
-                if (m < 121)
-                {
-                    width = 4;
-                    reqPreCompLen = 4;
-                }
-                else
-                {
-                    if (m < 337)
-                    {
-                        width = 5;
-                        reqPreCompLen = 8;
-                    }
-                    else
-                    {
-                        if (m < 897)
-                        {
-                            width = 6;
-                            reqPreCompLen = 16;
-                        }
-                        else
-                        {
-                            if (m < 2305)
-                            {
-                                width = 7;
-                                reqPreCompLen = 32;
-                            }
-                            else
-                            {
-                                width = 8;
-                                reqPreCompLen = 127;
-                            }
-                        }
-                    }
-                }
-            }
-        }
-
-        // The length of the precomputation array
-        int preCompLen = 1;
-
-        ECPoint[] preComp = wnafPreCompInfo.getPreComp();
-        ECPoint twiceP = wnafPreCompInfo.getTwiceP();
-
-        // Check if the precomputed ECPoints already exist
-        if (preComp == null)
-        {
-            // Precomputation must be performed from scratch, create an empty
-            // precomputation array of desired length
-            preComp = new ECPoint[]{ p };
-        }
-        else
-        {
-            // Take the already precomputed ECPoints to start with
-            preCompLen = preComp.length;
-        }
-
-        if (twiceP == null)
-        {
-            // Compute twice(p)
-            twiceP = p.twice();
-        }
-
-        if (preCompLen < reqPreCompLen)
-        {
-            // Precomputation array must be made bigger, copy existing preComp
-            // array into the larger new preComp array
-            ECPoint[] oldPreComp = preComp;
-            preComp = new ECPoint[reqPreCompLen];
-            System.arraycopy(oldPreComp, 0, preComp, 0, preCompLen);
-
-            for (int i = preCompLen; i < reqPreCompLen; i++)
-            {
-                // Compute the new ECPoints for the precomputation array.
-                // The values 1, 3, 5, ..., 2^(width-1)-1 times p are
-                // computed
-                preComp[i] = twiceP.add(preComp[i - 1]);
-            }            
-        }
-
-        // Compute the Window NAF of the desired width
-        byte[] wnaf = windowNaf(width, k);
-        int l = wnaf.length;
-
-        // Apply the Window NAF to p using the precomputed ECPoint values.
-        ECPoint q = p.getCurve().getInfinity();
-        for (int i = l - 1; i >= 0; i--)
-        {
-            q = q.twice();
-
-            if (wnaf[i] != 0)
-            {
-                if (wnaf[i] > 0)
-                {
-                    q = q.add(preComp[(wnaf[i] - 1)/2]);
-                }
-                else
-                {
-                    // wnaf[i] < 0
-                    q = q.subtract(preComp[(-wnaf[i] - 1)/2]);
-                }
-            }
-        }
-
-        // Set PreCompInfo in ECPoint, such that it is available for next
-        // multiplication.
-        wnafPreCompInfo.setPreComp(preComp);
-        wnafPreCompInfo.setTwiceP(twiceP);
-        p.setPreCompInfo(wnafPreCompInfo);
-        return q;
-    }
-
-}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java
index fc0d5fe..d142ab7 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafPreCompInfo.java
@@ -4,21 +4,23 @@
  * Class holding precomputation data for the WNAF (Window Non-Adjacent Form)
  * algorithm.
  */
-class WNafPreCompInfo implements PreCompInfo
+public class WNafPreCompInfo implements PreCompInfo
 {
     /**
-     * Array holding the precomputed <code>ECPoint</code>s used for the Window
-     * NAF multiplication in <code>
-     * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply()
-     * WNafMultiplier.multiply()}</code>.
+     * Array holding the precomputed <code>ECPoint</code>s used for a Window
+     * NAF multiplication.
      */
     private ECPoint[] preComp = null;
 
     /**
+     * Array holding the negations of the precomputed <code>ECPoint</code>s used
+     * for a Window NAF multiplication.
+     */
+    private ECPoint[] preCompNeg = null;
+
+    /**
      * Holds an <code>ECPoint</code> representing twice(this). Used for the
-     * Window NAF multiplication in <code>
-     * {@link org.bouncycastle.math.ec.multiplier.WNafMultiplier.multiply()
-     * WNafMultiplier.multiply()}</code>.
+     * Window NAF multiplication to create or extend the precomputed values.
      */
     private ECPoint twiceP = null;
 
@@ -27,18 +29,28 @@
         return preComp;
     }
 
+    protected ECPoint[] getPreCompNeg()
+    {
+        return preCompNeg;
+    }
+
     protected void setPreComp(ECPoint[] preComp)
     {
         this.preComp = preComp;
     }
 
+    protected void setPreCompNeg(ECPoint[] preCompNeg)
+    {
+        this.preCompNeg = preCompNeg;
+    }
+
     protected ECPoint getTwiceP()
     {
         return twiceP;
     }
 
-    protected void setTwiceP(ECPoint twiceThis)
+    protected void setTwiceP(ECPoint twiceP)
     {
-        this.twiceP = twiceThis;
+        this.twiceP = twiceP;
     }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java
new file mode 100644
index 0000000..6465d66
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WNafUtil.java
@@ -0,0 +1,393 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public abstract class WNafUtil
+{
+    private static int[] DEFAULT_WINDOW_SIZE_CUTOFFS = new int[]{ 13, 41, 121, 337, 897, 2305 };
+
+    public static int[] generateCompactNaf(BigInteger k)
+    {
+        if ((k.bitLength() >>> 16) != 0)
+        {
+            throw new IllegalArgumentException("'k' must have bitlength < 2^16");
+        }
+
+        BigInteger _3k = k.shiftLeft(1).add(k);
+
+        int digits = _3k.bitLength() - 1;
+        int[] naf = new int[(digits + 1) >> 1];
+
+        int length = 0, zeroes = 0;
+        for (int i = 1; i <= digits; ++i)
+        {
+            boolean _3kBit = _3k.testBit(i);
+            boolean kBit = k.testBit(i);
+
+            if (_3kBit == kBit)
+            {
+                ++zeroes;
+            }
+            else
+            {
+                int digit  = kBit ? -1 : 1;
+                naf[length++] = (digit << 16) | zeroes;
+                zeroes = 0;
+            }
+        }
+
+        if (naf.length > length)
+        {
+            naf = trim(naf, length);
+        }
+
+        return naf;
+    }
+
+    public static int[] generateCompactWindowNaf(int width, BigInteger k)
+    {
+        if (width == 2)
+        {
+            return generateCompactNaf(k);
+        }
+
+        if (width < 2 || width > 16)
+        {
+            throw new IllegalArgumentException("'width' must be in the range [2, 16]");
+        }
+        if ((k.bitLength() >>> 16) != 0)
+        {
+            throw new IllegalArgumentException("'k' must have bitlength < 2^16");
+        }
+
+        int[] wnaf = new int[k.bitLength() / width + 1];
+
+        // 2^width and a mask and sign bit set accordingly
+        int pow2 = 1 << width;
+        int mask = pow2 - 1;
+        int sign = pow2 >>> 1;
+
+        boolean carry = false;
+        int length = 0, pos = 0;
+
+        while (pos <= k.bitLength())
+        {
+            if (k.testBit(pos) == carry)
+            {
+                ++pos;
+                continue;
+            }
+
+            k = k.shiftRight(pos);
+
+            int digit = k.intValue() & mask;
+            if (carry)
+            {
+                ++digit;
+            }
+
+            carry = (digit & sign) != 0;
+            if (carry)
+            {
+                digit -= pow2;
+            }
+
+            int zeroes = length > 0 ? pos - 1 : pos;
+            wnaf[length++] = (digit << 16) | zeroes;
+            pos = width;
+        }
+
+        // Reduce the WNAF array to its actual length
+        if (wnaf.length > length)
+        {
+            wnaf = trim(wnaf, length);
+        }
+
+        return wnaf;
+    }
+
+    public static byte[] generateJSF(BigInteger g, BigInteger h)
+    {
+        int digits = Math.max(g.bitLength(), h.bitLength()) + 1;
+        byte[] jsf = new byte[digits];
+
+        BigInteger k0 = g, k1 = h;
+        int j = 0, d0 = 0, d1 = 0;
+
+        while (k0.signum() > 0 || k1.signum() > 0 || d0 > 0 || d1 > 0)
+        {
+            int n0 = (k0.intValue() + d0) & 7, n1 = (k1.intValue() + d1) & 7;
+
+            int u0 = n0 & 1;
+            if (u0 != 0)
+            {
+                u0 -= (n0 & 2);
+                if ((n0 + u0) == 4 && (n1 & 3) == 2)
+                {
+                    u0 = -u0;
+                }
+            }
+
+            int u1 = n1 & 1;
+            if (u1 != 0)
+            {
+                u1 -= (n1 & 2);
+                if ((n1 + u1) == 4 && (n0 & 3) == 2)
+                {
+                    u1 = -u1;
+                }
+            }
+
+            if ((d0 << 1) == 1 + u0)
+            {
+                d0 = 1 - d0;
+            }
+            if ((d1 << 1) == 1 + u1)
+            {
+                d1 = 1 - d1;
+            }
+
+            k0 = k0.shiftRight(1);
+            k1 = k1.shiftRight(1);
+
+            jsf[j++] = (byte)((u0 << 4) | (u1 & 0xF));
+        }
+
+        // Reduce the JSF array to its actual length
+        if (jsf.length > j)
+        {
+            jsf = trim(jsf, j);
+        }
+
+        return jsf;
+    }
+
+    public static byte[] generateNaf(BigInteger k)
+    {
+        BigInteger _3k = k.shiftLeft(1).add(k);
+
+        int digits = _3k.bitLength() - 1;
+        byte[] naf = new byte[digits];
+
+        for (int i = 1; i <= digits; ++i)
+        {
+            boolean _3kBit = _3k.testBit(i);
+            boolean kBit = k.testBit(i);
+
+            naf[i - 1] = (byte)(_3kBit == kBit ? 0 : kBit ? -1 : 1);
+        }
+
+        return naf;
+    }
+
+    /**
+     * Computes the Window NAF (non-adjacent Form) of an integer.
+     * @param width The width <code>w</code> of the Window NAF. The width is
+     * defined as the minimal number <code>w</code>, such that for any
+     * <code>w</code> consecutive digits in the resulting representation, at
+     * most one is non-zero.
+     * @param k The integer of which the Window NAF is computed.
+     * @return The Window NAF of the given width, such that the following holds:
+     * <code>k = &sum;<sub>i=0</sub><sup>l-1</sup> k<sub>i</sub>2<sup>i</sup>
+     * </code>, where the <code>k<sub>i</sub></code> denote the elements of the
+     * returned <code>byte[]</code>.
+     */
+    public static byte[] generateWindowNaf(int width, BigInteger k)
+    {
+        if (width == 2)
+        {
+            return generateNaf(k);
+        }
+
+        if (width < 2 || width > 8)
+        {
+            throw new IllegalArgumentException("'width' must be in the range [2, 8]");
+        }
+
+        byte[] wnaf = new byte[k.bitLength() + 1];
+
+        // 2^width and a mask and sign bit set accordingly
+        int pow2 = 1 << width;
+        int mask = pow2 - 1;
+        int sign = pow2 >>> 1;
+
+        boolean carry = false;
+        int length = 0, pos = 0;
+
+        while (pos <= k.bitLength())
+        {
+            if (k.testBit(pos) == carry)
+            {
+                ++pos;
+                continue;
+            }
+
+            k = k.shiftRight(pos);
+
+            int digit = k.intValue() & mask;
+            if (carry)
+            {
+                ++digit;
+            }
+
+            carry = (digit & sign) != 0;
+            if (carry)
+            {
+                digit -= pow2;
+            }
+
+            length += (length > 0) ? pos - 1 : pos;
+            wnaf[length++] = (byte)digit;
+            pos = width;
+        }
+
+        // Reduce the WNAF array to its actual length
+        if (wnaf.length > length)
+        {
+            wnaf = trim(wnaf, length);
+        }
+        
+        return wnaf;
+    }
+
+    public static WNafPreCompInfo getWNafPreCompInfo(PreCompInfo preCompInfo)
+    {
+        if ((preCompInfo != null) && (preCompInfo instanceof WNafPreCompInfo))
+        {
+            return (WNafPreCompInfo)preCompInfo;
+        }
+
+        return new WNafPreCompInfo();
+    }
+
+    /**
+     * Determine window width to use for a scalar multiplication of the given size.
+     * 
+     * @param bits the bit-length of the scalar to multiply by
+     * @return the window size to use
+     */
+    public static int getWindowSize(int bits)
+    {
+        return getWindowSize(bits, DEFAULT_WINDOW_SIZE_CUTOFFS);
+    }
+
+    /**
+     * Determine window width to use for a scalar multiplication of the given size.
+     * 
+     * @param bits the bit-length of the scalar to multiply by
+     * @param windowSizeCutoffs a monotonically increasing list of bit sizes at which to increment the window width
+     * @return the window size to use
+     */
+    public static int getWindowSize(int bits, int[] windowSizeCutoffs)
+    {
+        int w = 0;
+        for (; w < windowSizeCutoffs.length; ++w)
+        {
+            if (bits < windowSizeCutoffs[w])
+            {
+                break;
+            }
+        }
+        return w + 2;
+    }
+
+    public static WNafPreCompInfo precompute(ECPoint p, int width, boolean includeNegated)
+    {
+        ECCurve c = p.getCurve();
+        WNafPreCompInfo wnafPreCompInfo = getWNafPreCompInfo(c.getPreCompInfo(p));
+
+        ECPoint[] preComp = wnafPreCompInfo.getPreComp();
+        if (preComp == null)
+        {
+            preComp = new ECPoint[]{ p };
+        }
+
+        int preCompLen = preComp.length;
+        int reqPreCompLen = 1 << Math.max(0, width - 2);
+
+        if (preCompLen < reqPreCompLen)
+        {
+            ECPoint twiceP = wnafPreCompInfo.getTwiceP();
+            if (twiceP == null)
+            {
+                twiceP = preComp[0].twice().normalize();
+                wnafPreCompInfo.setTwiceP(twiceP);
+            }
+
+            preComp = resizeTable(preComp, reqPreCompLen);
+
+            /*
+             * TODO Okeya/Sakurai paper has precomputation trick and  "Montgomery's Trick" to speed this up.
+             * Also, co-Z arithmetic could avoid the subsequent normalization too.
+             */
+            for (int i = preCompLen; i < reqPreCompLen; i++)
+            {
+                /*
+                 * Compute the new ECPoints for the precomputation array. The values 1, 3, 5, ...,
+                 * 2^(width-1)-1 times p are computed
+                 */
+                preComp[i] = twiceP.add(preComp[i - 1]);
+            }
+
+            /*
+             * Having oft-used operands in affine form makes operations faster.
+             */
+            c.normalizeAll(preComp);
+        }
+
+        wnafPreCompInfo.setPreComp(preComp);
+
+        if (includeNegated)
+        {
+            ECPoint[] preCompNeg = wnafPreCompInfo.getPreCompNeg();
+            
+            int pos;
+            if (preCompNeg == null)
+            {
+                pos = 0;
+                preCompNeg = new ECPoint[reqPreCompLen]; 
+            }
+            else
+            {
+                pos = preCompNeg.length;
+                if (pos < reqPreCompLen)
+                {
+                    preCompNeg = resizeTable(preCompNeg, reqPreCompLen);
+                }
+            }
+
+            while (pos < reqPreCompLen)
+            {
+                preCompNeg[pos] = preComp[pos].negate();
+                ++pos;
+            }
+
+            wnafPreCompInfo.setPreCompNeg(preCompNeg);
+        }
+
+        c.setPreCompInfo(p, wnafPreCompInfo);
+
+        return wnafPreCompInfo;
+    }
+
+    private static byte[] trim(byte[] a, int length)
+    {
+        byte[] result = new byte[length];
+        System.arraycopy(a, 0, result, 0, result.length);
+        return result;
+    }
+
+    private static int[] trim(int[] a, int length)
+    {
+        int[] result = new int[length];
+        System.arraycopy(a, 0, result, 0, result.length);
+        return result;
+    }
+
+    private static ECPoint[] resizeTable(ECPoint[] a, int length)
+    {
+        ECPoint[] result = new ECPoint[length];
+        System.arraycopy(a, 0, result, 0, a.length);
+        return result;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
index 2353979..7bd30ec 100644
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/WTauNafMultiplier.java
@@ -6,7 +6,7 @@
  * Class implementing the WTNAF (Window
  * <code>&tau;</code>-adic Non-Adjacent Form) algorithm.
  */
-class WTauNafMultiplier implements ECMultiplier
+public class WTauNafMultiplier extends AbstractECMultiplier
 {
     /**
      * Multiplies a {@link org.bouncycastle.math.ec.ECPoint.F2m ECPoint.F2m}
@@ -16,7 +16,7 @@
      * @param k The integer by which to multiply <code>k</code>.
      * @return <code>p</code> multiplied by <code>k</code>.
      */
-    public ECPoint multiply(ECPoint point, BigInteger k, PreCompInfo preCompInfo)
+    protected ECPoint multiplyPositive(ECPoint point, BigInteger k)
     {
         if (!(point instanceof ECPoint.F2m))
         {
@@ -25,8 +25,7 @@
         }
 
         ECPoint.F2m p = (ECPoint.F2m)point;
-
-        ECCurve.F2m curve = (ECCurve.F2m) p.getCurve();
+        ECCurve.F2m curve = (ECCurve.F2m)p.getCurve();
         int m = curve.getM();
         byte a = curve.getA().toBigInteger().byteValue();
         byte mu = curve.getMu();
@@ -34,7 +33,7 @@
 
         ZTauElement rho = Tnaf.partModReduction(k, m, a, s, mu, (byte)10);
 
-        return multiplyWTnaf(p, rho, preCompInfo, a, mu);
+        return multiplyWTnaf(p, rho, curve.getPreCompInfo(p), a, mu);
     }
 
     /**
@@ -88,7 +87,7 @@
         if ((preCompInfo == null) || !(preCompInfo instanceof WTauNafPreCompInfo))
         {
             pu = Tnaf.getPreComp(p, a);
-            p.setPreCompInfo(new WTauNafPreCompInfo(pu));
+            curve.setPreCompInfo(p, new WTauNafPreCompInfo(pu));
         }
         else
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitL2RMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitL2RMultiplier.java
new file mode 100644
index 0000000..b478dc7
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitL2RMultiplier.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ZSignedDigitL2RMultiplier extends AbstractECMultiplier
+{
+    /**
+     * 'Zeroless' Signed Digit Left-to-Right.
+     */
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        ECPoint addP = p.normalize(), subP = addP.negate();
+
+        ECPoint R0 = addP;
+
+        int n = k.bitLength();
+        int s = k.getLowestSetBit();
+
+        int i = n;
+        while (--i > s)
+        {
+            R0 = R0.twicePlus(k.testBit(i) ? addP : subP);
+        }
+
+        R0 = R0.timesPow2(s);
+
+        return R0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitR2LMultiplier.java b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitR2LMultiplier.java
new file mode 100644
index 0000000..baa702f
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/math/ec/ZSignedDigitR2LMultiplier.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.math.ec;
+
+import java.math.BigInteger;
+
+public class ZSignedDigitR2LMultiplier extends AbstractECMultiplier
+{
+    /**
+     * 'Zeroless' Signed Digit Right-to-Left.
+     */
+    protected ECPoint multiplyPositive(ECPoint p, BigInteger k)
+    {
+        ECPoint R0 = p.getCurve().getInfinity(), R1 = p;
+
+        int n = k.bitLength();
+        int s = k.getLowestSetBit();
+
+        R1 = R1.timesPow2(s);
+
+        int i = s;
+        while (++i < n)
+        {
+            R0 = R0.add(k.testBit(i) ? R1 : R1.negate());
+            R1 = R1.twice();
+        }
+
+        R0 = R0.add(R1);
+
+        return R0;
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/math/ec/package.html b/bcprov/src/main/java/org/bouncycastle/math/ec/package.html
deleted file mode 100644
index a02605b..0000000
--- a/bcprov/src/main/java/org/bouncycastle/math/ec/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Math support for Elliptic Curve.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/ocsp/package.html b/bcprov/src/main/java/org/bouncycastle/ocsp/package.html
deleted file mode 100644
index 2498f2e..0000000
--- a/bcprov/src/main/java/org/bouncycastle/ocsp/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<b>Deprecated</b>: see the bcpkix distribution (org.bouncycastle.cert.ocsp), classes for dealing Online Certificate Status Protocol (OCSP) - RFC 2560.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
index b97a8f3..d93d995 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/asn1/PQCObjectIdentifiers.java
@@ -2,26 +2,45 @@
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 
+/**
+ * PQC:
+ * <p>
+ * { iso(1) identifier-organization(3) dod(6) internet(1) private(4) 1 8301 3 1 3 5 3 ... }
+ */
 public interface PQCObjectIdentifiers
 {
+    /** 1.3.6.1.4.1.8301.3.1.3.5.3.2 */
     public static final ASN1ObjectIdentifier rainbow = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.5.3.2");
 
-    public static final ASN1ObjectIdentifier rainbowWithSha1 = rainbow.branch("1");
+    /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.1 */
+    public static final ASN1ObjectIdentifier rainbowWithSha1   = rainbow.branch("1");
+    /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.2 */
     public static final ASN1ObjectIdentifier rainbowWithSha224 = rainbow.branch("2");
+    /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.3 */
     public static final ASN1ObjectIdentifier rainbowWithSha256 = rainbow.branch("3");
+    /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.4 */
     public static final ASN1ObjectIdentifier rainbowWithSha384 = rainbow.branch("4");
+    /** 1.3.6.1.4.1.8301.3.1.3.5.3.2.5 */
     public static final ASN1ObjectIdentifier rainbowWithSha512 = rainbow.branch("5");
 
+    /** 1.3.6.1.4.1.8301.3.1.3.3 */
     public static final ASN1ObjectIdentifier gmss = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.3");
 
-    public static final ASN1ObjectIdentifier gmssWithSha1 = gmss.branch("1");
+    /** 1.3.6.1.4.1.8301.3.1.3.3.1 */
+    public static final ASN1ObjectIdentifier gmssWithSha1   = gmss.branch("1");
+    /** 1.3.6.1.4.1.8301.3.1.3.3.2 */
     public static final ASN1ObjectIdentifier gmssWithSha224 = gmss.branch("2");
+    /** 1.3.6.1.4.1.8301.3.1.3.3.3 */
     public static final ASN1ObjectIdentifier gmssWithSha256 = gmss.branch("3");
+    /** 1.3.6.1.4.1.8301.3.1.3.3.4 */
     public static final ASN1ObjectIdentifier gmssWithSha384 = gmss.branch("4");
+    /** 1.3.6.1.4.1.8301.3.1.3.3.5 */
     public static final ASN1ObjectIdentifier gmssWithSha512 = gmss.branch("5");
 
-    public static final ASN1ObjectIdentifier mcEliece = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.1");
+    /** 1.3.6.1.4.1.8301.3.1.3.4.1 */
+    public static final ASN1ObjectIdentifier mcEliece       = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.1");
 
-    public static final ASN1ObjectIdentifier mcElieceCca2 = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.2");
+    /** 1.3.6.1.4.1.8301.3.1.3.4.2 */
+    public static final ASN1ObjectIdentifier mcElieceCca2   = new ASN1ObjectIdentifier("1.3.6.1.4.1.8301.3.1.3.4.2");
 
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java
index 51aa026..340f032 100644
--- a/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java
+++ b/bcprov/src/main/java/org/bouncycastle/pqc/jcajce/provider/BouncyCastlePQCProvider.java
@@ -21,7 +21,7 @@
     extends Provider
     implements ConfigurableProvider
 {
-    private static String info = "BouncyCastle Post-Quantum Security Provider v1.48";
+    private static String info = "BouncyCastle Post-Quantum Security Provider v1.50";
 
     public static String PROVIDER_NAME = "BCPQC";
 
@@ -46,7 +46,7 @@
      */
     public BouncyCastlePQCProvider()
     {
-        super(PROVIDER_NAME, 1.48, info);
+        super(PROVIDER_NAME, 1.50, info);
 
         AccessController.doPrivileged(new PrivilegedAction()
         {
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
index 457320e..3f7677c 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/Arrays.java
@@ -11,7 +11,7 @@
     {
         // static class, hide constructor
     }
-    
+
     public static boolean areEqual(
         boolean[]  a,
         boolean[]  b)
@@ -199,36 +199,62 @@
         return true;
     }
 
-    public static boolean areEqual(
-        BigInteger[]  a,
-        BigInteger[]  b)
+    public static boolean areEqual(Object[] a, Object[] b)
     {
         if (a == b)
         {
             return true;
         }
-
         if (a == null || b == null)
         {
             return false;
         }
-
         if (a.length != b.length)
         {
             return false;
         }
-
         for (int i = 0; i != a.length; i++)
         {
-            if (!a[i].equals(b[i]))
+            Object objA = a[i], objB = b[i];
+            if (objA == null)
+            {
+                if (objB != null)
+                {
+                    return false;
+                }
+            }
+            else if (!objA.equals(objB))
             {
                 return false;
             }
         }
-
         return true;
     }
 
+    public static boolean contains(short[] a, short n)
+    {
+        for (int i = 0; i < a.length; ++i)
+        {
+            if (a[i] == n)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    public static boolean contains(int[] a, int n)
+    {
+        for (int i = 0; i < a.length; ++i)
+        {
+            if (a[i] == n)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
     public static void fill(
         byte[] array,
         byte value)
@@ -391,7 +417,7 @@
         return hc;
     }
 
-    public static int hashCode(BigInteger[] data)
+    public static int hashCode(Object[] data)
     {
         if (data == null)
         {
@@ -423,6 +449,20 @@
         return copy;
     }
 
+    public static byte[] clone(byte[] data, byte[] existing)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+        if ((existing == null) || (existing.length != data.length))
+        {
+            return clone(data);
+        }
+        System.arraycopy(data, 0, existing, 0, existing.length);
+        return existing;
+    }
+
     public static byte[][] clone(byte[][] data)
     {
         if (data == null)
@@ -470,6 +510,33 @@
         return copy;
     }
 
+    public static long[] clone(long[] data)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+        long[] copy = new long[data.length];
+        
+        System.arraycopy(data, 0, copy, 0, data.length);
+        
+        return copy;
+    }
+
+    public static long[] clone(long[] data, long[] existing)
+    {
+        if (data == null)
+        {
+            return null;
+        }
+        if ((existing == null) || (existing.length != data.length))
+        {
+            return clone(data);
+        }
+        System.arraycopy(data, 0, existing, 0, existing.length);
+        return existing;
+    }
+
     public static short[] clone(short[] data)
     {
         if (data == null)
@@ -576,6 +643,17 @@
         return tmp;
     }
 
+    /**
+     * Make a copy of a range of bytes from the passed in data array. The range can
+     * extend beyond the end of the input array, in which case the return array will
+     * be padded with zeroes.
+     *
+     * @param data the array from which the data is to be copied.
+     * @param from the start index at which the copying should take place.
+     * @param to the final index of the range (exclusive).
+     *
+     * @return a new byte array containing the range given.
+     */
     public static byte[] copyOfRange(byte[] data, int from, int to)
     {
         int newLength = getLength(from, to);
@@ -660,6 +738,34 @@
         return newLength;
     }
 
+    public static byte[] append(byte[] a, byte b)
+    {
+        if (a == null)
+        {
+            return new byte[]{ b };
+        }
+
+        int length = a.length;
+        byte[] result = new byte[length + 1];
+        System.arraycopy(a, 0, result, 0, length);
+        result[length] = b;
+        return result;
+    }
+
+    public static int[] append(int[] a, int b)
+    {
+        if (a == null)
+        {
+            return new int[]{ b };
+        }
+
+        int length = a.length;
+        int[] result = new int[length + 1];
+        System.arraycopy(a, 0, result, 0, length);
+        result[length] = b;
+        return result;
+    }
+
     public static byte[] concatenate(byte[] a, byte[] b)
     {
         if (a != null && b != null)
@@ -733,4 +839,18 @@
             return concatenate(b, c, d);
         }
     }
+
+    public static byte[] prepend(byte[] a, byte b)
+    {
+        if (a == null)
+        {
+            return new byte[]{ b };
+        }
+
+        int length = a.length;
+        byte[] result = new byte[length + 1];
+        System.arraycopy(a, 0, result, 1, length);
+        result[0] = b;
+        return result;
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
index e2fe590..f7f7e68 100644
--- a/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
+++ b/bcprov/src/main/java/org/bouncycastle/util/BigIntegers.java
@@ -40,43 +40,25 @@
      * @param value value to be converted.
      * @return a byte array without a leading zero byte if present in the signed encoding.
      */
-    public static byte[] asUnsignedByteArray(
-        int        length,
-        BigInteger value)
+    public static byte[] asUnsignedByteArray(int length, BigInteger value)
     {
         byte[] bytes = value.toByteArray();
-
-        if (bytes[0] == 0)
+        if (bytes.length == length)
         {
-            if (bytes.length - 1 > length)
-            {
-                throw new IllegalArgumentException("standard length exceeded for value");
-            }
-
-            byte[] tmp = new byte[length];
-
-            System.arraycopy(bytes, 1, tmp, tmp.length - (bytes.length - 1), bytes.length - 1);
-
-            return tmp;
+            return bytes;
         }
-        else
+
+        int start = bytes[0] == 0 ? 1 : 0;
+        int count = bytes.length - start;
+
+        if (count > length)
         {
-            if (bytes.length == length)
-            {
-                return bytes;
-            }
-
-            if (bytes.length > length)
-            {
-                throw new IllegalArgumentException("standard length exceeded for value");
-            }
-
-            byte[] tmp = new byte[length];
-
-            System.arraycopy(bytes, 0, tmp, tmp.length - bytes.length, bytes.length);
-
-            return tmp;
+            throw new IllegalArgumentException("standard length exceeded for value");
         }
+
+        byte[] tmp = new byte[length];
+        System.arraycopy(bytes, start, tmp, tmp.length - count, count);
+        return tmp;
     }
 
     /**
@@ -120,4 +102,20 @@
         // fall back to a faster (restricted) method
         return new BigInteger(max.subtract(min).bitLength() - 1, random).add(min);
     }
+
+    public static BigInteger fromUnsignedByteArray(byte[] buf)
+    {
+        return new BigInteger(1, buf);
+    }
+
+    public static BigInteger fromUnsignedByteArray(byte[] buf, int off, int length)
+    {
+        byte[] mag = buf;
+        if (off != 0 || length != buf.length)
+        {
+            mag = new byte[length];
+            System.arraycopy(buf, off, mag, 0, length);
+        }
+        return new BigInteger(1, mag);
+    }
 }
diff --git a/bcprov/src/main/java/org/bouncycastle/util/Shorts.java b/bcprov/src/main/java/org/bouncycastle/util/Shorts.java
new file mode 100644
index 0000000..258e01e
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/Shorts.java
@@ -0,0 +1,9 @@
+package org.bouncycastle.util;
+
+public class Shorts
+{
+    public static Short valueOf(short value)
+    {
+        return Short.valueOf(value);
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/util/encoders/package.html b/bcprov/src/main/java/org/bouncycastle/util/encoders/package.html
deleted file mode 100644
index 3be222b..0000000
--- a/bcprov/src/main/java/org/bouncycastle/util/encoders/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-Classes for producing and reading Base64 and Hex strings.
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/util/io/BufferingOutputStream.java b/bcprov/src/main/java/org/bouncycastle/util/io/BufferingOutputStream.java
new file mode 100644
index 0000000..9d5fe14
--- /dev/null
+++ b/bcprov/src/main/java/org/bouncycastle/util/io/BufferingOutputStream.java
@@ -0,0 +1,108 @@
+package org.bouncycastle.util.io;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.Arrays;
+
+/**
+ * An output stream that buffers data to be feed into an encapsulated output stream.
+ * <p>
+ * The stream zeroes out the internal buffer on each flush.
+ * </p>
+ */
+public class BufferingOutputStream
+    extends OutputStream
+{
+    private final OutputStream other;
+    private final byte[] buf;
+
+    private int   bufOff;
+
+    /**
+     * Create a buffering stream with the default buffer size (4096).
+     *
+     * @param other output stream to be wrapped.
+     */
+    public BufferingOutputStream(OutputStream other)
+    {
+        this.other = other;
+        this.buf = new byte[4096];
+    }
+
+    /**
+     * Create a buffering stream with a specified buffer size.
+     *
+     * @param other output stream to be wrapped.
+     * @param bufferSize size in bytes for internal buffer.
+     */
+    public BufferingOutputStream(OutputStream other, int bufferSize)
+    {
+        this.other = other;
+        this.buf = new byte[bufferSize];
+    }
+
+    public void write(byte[] bytes, int offset, int len)
+        throws IOException
+    {
+        if (len < buf.length - bufOff)
+        {
+            System.arraycopy(bytes, offset, buf, bufOff, len);
+            bufOff += len;
+        }
+        else
+        {
+            int gap = buf.length - bufOff;
+
+            System.arraycopy(bytes, offset, buf, bufOff, gap);
+            bufOff += gap;
+
+            flush();
+
+            offset += gap;
+            len -= gap;
+            while (len >= buf.length)
+            {
+                other.write(bytes, offset, buf.length);
+                offset += buf.length;
+                len -= buf.length;
+            }
+
+            if (len > 0)
+            {
+                System.arraycopy(bytes, offset, buf, bufOff, len);
+                bufOff += len;
+            }
+        }
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        buf[bufOff++] = (byte)b;
+        if (bufOff == buf.length)
+        {
+            flush();
+        }
+    }
+
+    /**
+     * Flush the internal buffer to the encapsulated output stream. Zero the buffer contents when done.
+     *
+     * @throws IOException on error.
+     */
+    public void flush()
+        throws IOException
+    {
+        other.write(buf, 0, bufOff);
+        bufOff = 0;
+        Arrays.fill(buf, (byte)0);
+    }
+
+    public void close()
+        throws IOException
+    {
+        flush();
+        other.close();
+    }
+}
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java b/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java
index 1bfc00f..61d921c 100644
--- a/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java
+++ b/bcprov/src/main/java/org/bouncycastle/x509/X509Store.java
@@ -1,13 +1,16 @@
 package org.bouncycastle.x509;
 
-import org.bouncycastle.util.Selector;
-import org.bouncycastle.util.Store;
-
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.Provider;
 import java.util.Collection;
 
+import org.bouncycastle.util.Selector;
+import org.bouncycastle.util.Store;
+
+/**
+ * @deprecated use CollectionStore - this class will be removed.
+ */
 public class X509Store
     implements Store
 {
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/examples/package.html b/bcprov/src/main/java/org/bouncycastle/x509/examples/package.html
deleted file mode 100644
index 6262157..0000000
--- a/bcprov/src/main/java/org/bouncycastle/x509/examples/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<p>
-Examples for X.509 attribute certificates.
-<p>
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/extension/package.html b/bcprov/src/main/java/org/bouncycastle/x509/extension/package.html
deleted file mode 100644
index 8127aa5..0000000
--- a/bcprov/src/main/java/org/bouncycastle/x509/extension/package.html
+++ /dev/null
@@ -1,5 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<b>Deprecated:</b> see bcpkix distribution (org.bouncycastle.cert), helper classes for dealing with common X.509 extensions. 
-</body>
-</html>
diff --git a/bcprov/src/main/java/org/bouncycastle/x509/package.html b/bcprov/src/main/java/org/bouncycastle/x509/package.html
deleted file mode 100644
index be27c55..0000000
--- a/bcprov/src/main/java/org/bouncycastle/x509/package.html
+++ /dev/null
@@ -1,7 +0,0 @@
-<html>
-<body bgcolor="#ffffff">
-<p>
-<b>Deprecated:</b> see bcpkix distribution (org.bouncycastle.cert), classes for supporting the generation of X.509 certificates and X.509 attribute certificates. 
-<p>
-</body>
-</html>