bouncycastle: Android tree with upstream code for version 1.61

Test: no tests needed, this branch is only for diffing against upstream
Change-Id: I229752afcbb6c113248155b5dd969bd73b697e42
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
index fac9b44..082f582 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509AttributeCertificateHolder.java
@@ -1,7 +1,10 @@
 package org.bouncycastle.cert;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Date;
@@ -26,12 +29,14 @@
  * Holding class for an X.509 AttributeCertificate structure.
  */
 public class X509AttributeCertificateHolder
-    implements Encodable
+    implements Encodable, Serializable
 {
+    private static final long serialVersionUID = 20170722001L;
+
     private static Attribute[] EMPTY_ARRAY = new Attribute[0];
     
-    private AttributeCertificate attrCert;
-    private Extensions extensions;
+    private transient AttributeCertificate attrCert;
+    private transient Extensions extensions;
 
     private static AttributeCertificate parseBytes(byte[] certEncoding)
         throws IOException
@@ -69,6 +74,11 @@
      */
     public X509AttributeCertificateHolder(AttributeCertificate attrCert)
     {
+        init(attrCert);
+    }
+
+    private void init(AttributeCertificate attrCert)
+    {
         this.attrCert = attrCert;
         this.extensions = attrCert.getAcinfo().getExtensions();
     }
@@ -364,4 +374,22 @@
     {
         return this.attrCert.hashCode();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        init(AttributeCertificate.getInstance(in.readObject()));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
index 67abd31..ef89601 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CRLHolder.java
@@ -3,7 +3,10 @@
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.math.BigInteger;
 import java.util.ArrayList;
 import java.util.Collection;
@@ -31,12 +34,14 @@
  * Holding class for an X.509 CRL structure.
  */
 public class X509CRLHolder
-    implements Encodable
+    implements Encodable, Serializable
 {
-    private CertificateList x509CRL;
-    private boolean isIndirect;
-    private Extensions extensions;
-    private GeneralNames issuerName;
+    private static final long serialVersionUID = 20170722001L;
+    
+    private transient CertificateList x509CRL;
+    private transient boolean isIndirect;
+    private transient Extensions extensions;
+    private transient GeneralNames issuerName;
 
     private static CertificateList parseStream(InputStream stream)
         throws IOException
@@ -103,6 +108,11 @@
      */
     public X509CRLHolder(CertificateList x509CRL)
     {
+        init(x509CRL);
+    }
+
+    private void init(CertificateList x509CRL)
+    {
         this.x509CRL = x509CRL;
         this.extensions = x509CRL.getTBSCertList().getExtensions();
         this.isIndirect = isIndirectCRL(extensions);
@@ -322,4 +332,22 @@
     {
         return this.x509CRL.hashCode();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        init(CertificateList.getInstance(in.readObject()));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
index 2396aac..0fb8673 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/X509CertificateHolder.java
@@ -1,7 +1,10 @@
 package org.bouncycastle.cert;
 
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.OutputStream;
+import java.io.Serializable;
 import java.math.BigInteger;
 import java.util.Date;
 import java.util.List;
@@ -24,10 +27,12 @@
  * Holding class for an X.509 Certificate structure.
  */
 public class X509CertificateHolder
-    implements Encodable
+    implements Encodable, Serializable
 {
-    private Certificate x509Certificate;
-    private Extensions  extensions;
+    private static final long serialVersionUID = 20170722001L;
+
+    private transient Certificate x509Certificate;
+    private transient Extensions  extensions;
 
     private static Certificate parseBytes(byte[] certEncoding)
         throws IOException
@@ -65,6 +70,11 @@
      */
     public X509CertificateHolder(Certificate x509Certificate)
     {
+        init(x509Certificate);
+    }
+
+    private void init(Certificate x509Certificate)
+    {
         this.x509Certificate = x509Certificate;
         this.extensions = x509Certificate.getTBSCertificate().getExtensions();
     }
@@ -325,4 +335,22 @@
     {
         return x509Certificate.getEncoded();
     }
+
+    private void readObject(
+        ObjectInputStream in)
+        throws IOException, ClassNotFoundException
+    {
+        in.defaultReadObject();
+
+        init(Certificate.getInstance(in.readObject()));
+    }
+
+    private void writeObject(
+        ObjectOutputStream out)
+        throws IOException
+    {
+        out.defaultWriteObject();
+
+        out.writeObject(this.getEncoded());
+    }
 }
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 2763083..05d5946 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
@@ -15,16 +15,26 @@
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.cmp.CMPCertificate;
 import org.bouncycastle.asn1.cmp.CertConfirmContent;
+import org.bouncycastle.asn1.cmp.CertOrEncCert;
 import org.bouncycastle.asn1.cmp.CertRepMessage;
+import org.bouncycastle.asn1.cmp.CertResponse;
+import org.bouncycastle.asn1.cmp.CertifiedKeyPair;
 import org.bouncycastle.asn1.cmp.PKIBody;
 import org.bouncycastle.asn1.cmp.PKIMessage;
+import org.bouncycastle.asn1.cmp.PKIStatus;
+import org.bouncycastle.asn1.cmp.PKIStatusInfo;
 import org.bouncycastle.asn1.crmf.CertReqMessages;
 import org.bouncycastle.asn1.crmf.CertReqMsg;
+import org.bouncycastle.asn1.crmf.EncryptedValue;
 import org.bouncycastle.asn1.crmf.ProofOfPossession;
 import org.bouncycastle.asn1.crmf.SubsequentMessage;
+import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.cert.CertException;
@@ -40,16 +50,25 @@
 import org.bouncycastle.cert.crmf.CertificateRequestMessageBuilder;
 import org.bouncycastle.cert.crmf.PKMACBuilder;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessageBuilder;
+import org.bouncycastle.cert.crmf.jcajce.JcaEncryptedValueBuilder;
+import org.bouncycastle.cert.crmf.jcajce.JceCRMFEncryptorBuilder;
 import org.bouncycastle.cert.crmf.jcajce.JcePKMACValuesCalculator;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.operator.AsymmetricKeyUnwrapper;
 import org.bouncycastle.operator.ContentSigner;
 import org.bouncycastle.operator.ContentVerifierProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper;
+import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper;
+import org.bouncycastle.operator.jcajce.JceInputDecryptorProviderBuilder;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.io.Streams;
 
 public class AllTests
@@ -225,6 +244,77 @@
         assertEquals(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, reqMsg.getPopo().getType());
     }
 
+    public void testServerSideKey()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+        X509CertificateHolder cert = makeV3Certificate(kp, "CN=Test", kp, "CN=Test");
+
+        JcaEncryptedValueBuilder encBldr = new JcaEncryptedValueBuilder(
+            new JceAsymmetricKeyWrapper(kp.getPublic()).setProvider(BC),
+            new JceCRMFEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        GeneralName sender = new GeneralName(new X500Name("CN=Sender"));
+        GeneralName recipient = new GeneralName(new X500Name("CN=Recip"));
+
+        CertRepMessage msg = new CertRepMessage(null, new CertResponse[] {
+            new CertResponse(
+                new ASN1Integer(2),
+                new PKIStatusInfo(PKIStatus.granted),
+                new CertifiedKeyPair(
+                    new CertOrEncCert(CMPCertificate.getInstance(cert.getEncoded())),
+                    encBldr.build(kp.getPrivate()),
+                    null), null) });
+
+        ContentSigner signer = new JcaContentSignerBuilder("MD5WithRSAEncryption").setProvider(BC).build(kp.getPrivate());
+        ProtectedPKIMessage message = new ProtectedPKIMessageBuilder(sender, recipient)
+                                                  .setBody(new PKIBody(PKIBody.TYPE_INIT_REP, msg))
+                                                  .addCMPCertificate(cert)
+                                                  .build(signer);
+
+        X509Certificate jcaCert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(message.getCertificates()[0]);
+        ContentVerifierProvider verifierProvider = new JcaContentVerifierProviderBuilder().setProvider(BC).build(jcaCert.getPublicKey());
+
+        assertTrue(message.verify(verifierProvider));
+
+        assertEquals(sender, message.getHeader().getSender());
+        assertEquals(recipient, message.getHeader().getRecipient());
+
+        CertRepMessage content = CertRepMessage.getInstance(message.getBody().getContent());
+
+        CertResponse[] responseList = content.getResponse();
+
+        assertEquals(1, responseList.length);
+
+        CertResponse response = responseList[0];
+
+        assertEquals(PKIStatus.granted.getValue(), response.getStatus().getStatus());
+
+        CertifiedKeyPair certKp = response.getCertifiedKeyPair();
+
+        // steps to unwrap private key
+        EncryptedValue encValue = certKp.getPrivateKey();
+
+        // recover symmetric key
+        AsymmetricKeyUnwrapper unwrapper = new JceAsymmetricKeyUnwrapper(encValue.getKeyAlg(), kp.getPrivate());
+        
+        byte[] secKeyBytes = (byte[])unwrapper.generateUnwrappedKey(encValue.getKeyAlg(), encValue.getEncSymmKey().getBytes()).getRepresentation();
+
+        // recover private key
+        PKCS8EncryptedPrivateKeyInfo respInfo = new PKCS8EncryptedPrivateKeyInfo(
+            new EncryptedPrivateKeyInfo(encValue.getSymmAlg(), encValue.getEncValue().getBytes()));
+
+        PrivateKeyInfo keyInfo = respInfo.decryptPrivateKeyInfo(new JceInputDecryptorProviderBuilder().setProvider("BC").build(secKeyBytes));
+
+        assertEquals(keyInfo.getPrivateKeyAlgorithm(), encValue.getIntendedAlg());
+        assertTrue(Arrays.areEqual(kp.getPrivate().getEncoded(), keyInfo.getEncoded()));
+    }
+
+
     public void testNotBeforeNotAfter()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java
index 8ea6ecd..04673d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CRMFException.java
@@ -5,6 +5,11 @@
 {
     private Throwable cause;
 
+    public CRMFException(String msg)
+    {
+        this(msg, null);
+    }
+
     public CRMFException(String msg, Throwable cause)
     {
         super(msg);
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 aa48235..3294285 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/CertificateRequestMessageBuilder.java
@@ -13,12 +13,14 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.DERTaggedObject;
 import org.bouncycastle.asn1.crmf.AttributeTypeAndValue;
 import org.bouncycastle.asn1.crmf.CertReqMsg;
 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.PKMACValue;
 import org.bouncycastle.asn1.crmf.POPOPrivKey;
 import org.bouncycastle.asn1.crmf.ProofOfPossession;
 import org.bouncycastle.asn1.crmf.SubsequentMessage;
@@ -41,8 +43,10 @@
     private PKMACBuilder pkmacBuilder;
     private char[] password;
     private GeneralName sender;
+    private int popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
     private POPOPrivKey popoPrivKey;
     private ASN1Null popRaVerified;
+    private PKMACValue agreeMAC;
 
     public CertificateRequestMessageBuilder(BigInteger certReqId)
     {
@@ -148,7 +152,7 @@
 
     public CertificateRequestMessageBuilder setProofOfPossessionSigningKeySigner(ContentSigner popSigner)
     {
-        if (popoPrivKey != null || popRaVerified != null)
+        if (popoPrivKey != null || popRaVerified != null || agreeMAC != null)
         {
             throw new IllegalStateException("only one proof of possession allowed");
         }
@@ -160,16 +164,46 @@
 
     public CertificateRequestMessageBuilder setProofOfPossessionSubsequentMessage(SubsequentMessage msg)
     {
-        if (popSigner != null || popRaVerified != null)
+        if (popSigner != null || popRaVerified != null || agreeMAC != null)
         {
             throw new IllegalStateException("only one proof of possession allowed");
         }
 
+        this.popoType = ProofOfPossession.TYPE_KEY_ENCIPHERMENT;
         this.popoPrivKey = new POPOPrivKey(msg);
 
         return this;
     }
 
+    public CertificateRequestMessageBuilder setProofOfPossessionSubsequentMessage(int type, SubsequentMessage msg)
+    {
+        if (popSigner != null || popRaVerified != null || agreeMAC != null)
+        {
+            throw new IllegalStateException("only one proof of possession allowed");
+        }
+        if (type != ProofOfPossession.TYPE_KEY_ENCIPHERMENT && type != ProofOfPossession.TYPE_KEY_AGREEMENT)
+        {
+            throw new IllegalArgumentException("type must be ProofOfPossession.TYPE_KEY_ENCIPHERMENT || ProofOfPossession.TYPE_KEY_AGREEMENT");
+        }
+
+        this.popoType = type;
+        this.popoPrivKey = new POPOPrivKey(msg);
+
+        return this;
+    }
+
+    public CertificateRequestMessageBuilder setProofOfPossessionAgreeMAC(PKMACValue macValue)
+    {
+        if (popSigner != null || popRaVerified != null || popoPrivKey != null)
+        {
+            throw new IllegalStateException("only one proof of possession allowed");
+        }
+
+        this.agreeMAC = macValue;
+
+        return this;
+    }
+
     public CertificateRequestMessageBuilder setProofOfPossessionRaVerified()
     {
         if (popSigner != null || popoPrivKey != null)
@@ -267,7 +301,13 @@
         }
         else if (popoPrivKey != null)
         {
-            v.add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_ENCIPHERMENT, popoPrivKey));
+            v.add(new ProofOfPossession(popoType, popoPrivKey));
+        }
+        else if (agreeMAC != null)
+        {
+            v.add(new ProofOfPossession(ProofOfPossession.TYPE_KEY_AGREEMENT,
+                    POPOPrivKey.getInstance(new DERTaggedObject(false, POPOPrivKey.agreeMAC, agreeMAC))));
+
         }
         else if (popRaVerified != null)
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java
index 55187b5..c9aacc5 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueBuilder.java
@@ -7,11 +7,14 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERBitString;
 import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.KeyWrapper;
 import org.bouncycastle.operator.OperatorException;
 import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfo;
+import org.bouncycastle.pkcs.PKCS8EncryptedPrivateKeyInfoBuilder;
 import org.bouncycastle.util.Strings;
 
 /**
@@ -82,6 +85,44 @@
         }
     }
 
+    /**
+     * Build an EncryptedValue structure containing the private key contained in
+     * the passed info structure.
+     *
+     * @param privateKeyInfo  a PKCS#8 private key info structure.
+     * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(PrivateKeyInfo privateKeyInfo)
+        throws CRMFException
+    {
+        PKCS8EncryptedPrivateKeyInfoBuilder encInfoBldr = new PKCS8EncryptedPrivateKeyInfoBuilder(privateKeyInfo);
+
+        AlgorithmIdentifier intendedAlg = privateKeyInfo.getPrivateKeyAlgorithm();
+        AlgorithmIdentifier symmAlg = encryptor.getAlgorithmIdentifier();
+        DERBitString encSymmKey;
+
+        try
+        {
+            PKCS8EncryptedPrivateKeyInfo encInfo = encInfoBldr.build(encryptor);
+            
+            encSymmKey = new DERBitString(wrapper.generateWrappedKey(encryptor.getKey()));
+
+            AlgorithmIdentifier keyAlg = wrapper.getAlgorithmIdentifier();
+            ASN1OctetString valueHint = null;
+
+            return new EncryptedValue(intendedAlg, symmAlg, encSymmKey, keyAlg, valueHint, new DERBitString(encInfo.getEncryptedData()));
+        }
+        catch (IllegalStateException e)
+        {
+            throw new CRMFException("cannot encode key: " + e.getMessage(), e);
+        }
+        catch (OperatorException e)
+        {
+            throw new CRMFException("cannot wrap key: " + e.getMessage(), e);
+        }
+    }
+
     private EncryptedValue encryptData(byte[] data)
        throws CRMFException
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java
index 6c0aa87..3390064 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/EncryptedValueParser.java
@@ -5,6 +5,8 @@
 import java.io.InputStream;
 
 import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.Certificate;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.operator.InputDecryptor;
@@ -42,13 +44,14 @@
         this.padder = padder;
     }
 
+    public AlgorithmIdentifier getIntendedAlg()
+    {
+        return value.getIntendedAlg();
+    }
+
     private byte[] decryptValue(ValueDecryptorGenerator decGen)
         throws CRMFException
     {
-        if (value.getIntendedAlg() != null)
-        {
-            throw new UnsupportedOperationException();
-        }
         if (value.getValueHint() != null)
         {
             throw new UnsupportedOperationException();
@@ -89,6 +92,19 @@
     }
 
     /**
+     * Read a PKCS#8 PrivateKeyInfo.
+     *
+     * @param decGen the decryptor generator to decrypt the encrypted value.
+     * @return an PrivateKeyInfo containing the private key that was read.
+     * @throws CRMFException if the decrypted data cannot be parsed, or a decryptor cannot be generated.
+     */
+    public PrivateKeyInfo readPrivateKeyInfo(ValueDecryptorGenerator decGen)
+        throws CRMFException
+    {
+        return PrivateKeyInfo.getInstance(decryptValue(decGen));
+    }
+
+    /**
      * Read a pass phrase.
      *
      * @param decGen the decryptor generator to decrypt the encrypted value.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java
index 1d8c369..02073b3 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/PKMACValueVerifier.java
@@ -38,6 +38,6 @@
             throw new CRMFException("exception encoding mac input: " + e.getMessage(), e);
         }
 
-        return Arrays.areEqual(calculator.getMac(), value.getValue().getBytes());
+        return Arrays.constantTimeAreEqual(calculator.getMac(), value.getValue().getBytes());
     }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcCRMFEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcCRMFEncryptorBuilder.java
new file mode 100644
index 0000000..df40133
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcCRMFEncryptorBuilder.java
@@ -0,0 +1,88 @@
+package org.bouncycastle.cert.crmf.bc;
+
+import java.io.OutputStream;
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.crmf.CRMFException;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CryptoServicesRegistrar;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.CipherFactory;
+import org.bouncycastle.operator.GenericKey;
+import org.bouncycastle.operator.OutputEncryptor;
+
+/**
+ * Lightweight CRMFOutputEncryptor builder.
+ */
+public class BcCRMFEncryptorBuilder
+{
+    private final ASN1ObjectIdentifier encryptionOID;
+    private final int                  keySize;
+
+    private CRMFHelper helper = new CRMFHelper();
+    private SecureRandom random;
+
+    public BcCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID)
+    {
+        this(encryptionOID, -1);
+    }
+
+    public BcCRMFEncryptorBuilder(ASN1ObjectIdentifier encryptionOID, int keySize)
+    {
+        this.encryptionOID = encryptionOID;
+        this.keySize = keySize;
+    }
+
+    public BcCRMFEncryptorBuilder setSecureRandom(SecureRandom random)
+    {
+        this.random = random;
+
+        return this;
+    }
+
+    public OutputEncryptor build()
+        throws CRMFException
+    {
+        return new CRMFOutputEncryptor(encryptionOID, keySize, random);
+    }
+
+    private class CRMFOutputEncryptor
+        implements OutputEncryptor
+    {
+        private KeyParameter encKey;
+        private AlgorithmIdentifier algorithmIdentifier;
+        private Object cipher;
+
+        CRMFOutputEncryptor(ASN1ObjectIdentifier encryptionOID, int keySize, SecureRandom random)
+            throws CRMFException
+        {
+            if (random == null)
+            {
+                random = CryptoServicesRegistrar.getSecureRandom();
+            }
+
+            CipherKeyGenerator keyGen = helper.createKeyGenerator(encryptionOID, random);
+
+            encKey = new KeyParameter(keyGen.generateKey());
+            algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random);
+            cipher = helper.createContentCipher(true, encKey, algorithmIdentifier);
+        }
+
+        public AlgorithmIdentifier getAlgorithmIdentifier()
+        {
+            return algorithmIdentifier;
+        }
+
+        public OutputStream getOutputStream(OutputStream dOut)
+        {
+            return CipherFactory.createOutputStream(dOut, cipher);
+        }
+
+        public GenericKey getKey()
+        {
+            return new GenericKey(algorithmIdentifier, encKey.getKey());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcEncryptedValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcEncryptedValueBuilder.java
new file mode 100644
index 0000000..a39215c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/BcEncryptedValueBuilder.java
@@ -0,0 +1,53 @@
+package org.bouncycastle.cert.crmf.bc;
+
+import java.io.IOException;
+import java.security.cert.CertificateEncodingException;
+import java.security.cert.X509Certificate;
+
+import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.cert.crmf.CRMFException;
+import org.bouncycastle.cert.crmf.EncryptedValueBuilder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PrivateKeyInfoFactory;
+import org.bouncycastle.operator.KeyWrapper;
+import org.bouncycastle.operator.OutputEncryptor;
+
+/**
+ * Lightweight convenience class for EncryptedValueBuilder
+ */
+public class BcEncryptedValueBuilder
+    extends EncryptedValueBuilder
+{
+    public BcEncryptedValueBuilder(KeyWrapper wrapper, OutputEncryptor encryptor)
+    {
+        super(wrapper, encryptor);
+    }
+
+    /**
+     * Build an EncryptedValue structure containing the passed in certificate.
+     *
+     * @param certificate the certificate to be encrypted.
+     * @return an EncryptedValue containing the encrypted certificate.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(X509Certificate certificate)
+        throws CertificateEncodingException, CRMFException
+    {
+        return build(new JcaX509CertificateHolder(certificate));
+    }
+
+    /**
+     * Build an EncryptedValue structure containing the private key details contained in
+     * the passed PrivateKey.
+     *
+     * @param privateKey a private key parameter.
+     * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(AsymmetricKeyParameter privateKey)
+        throws CRMFException, IOException
+    {
+        return build(PrivateKeyInfoFactory.createPrivateKeyInfo(privateKey));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/CRMFHelper.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/CRMFHelper.java
new file mode 100644
index 0000000..eb36b97
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/bc/CRMFHelper.java
@@ -0,0 +1,59 @@
+package org.bouncycastle.cert.crmf.bc;
+
+import java.security.SecureRandom;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.crmf.CRMFException;
+import org.bouncycastle.crypto.CipherKeyGenerator;
+import org.bouncycastle.crypto.CipherParameters;
+import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.AlgorithmIdentifierFactory;
+import org.bouncycastle.crypto.util.CipherFactory;
+import org.bouncycastle.crypto.util.CipherKeyGeneratorFactory;
+
+class CRMFHelper
+{
+    CRMFHelper()
+    {
+    }
+
+    CipherKeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm, SecureRandom random)
+        throws CRMFException
+    {
+        try
+        {
+            return CipherKeyGeneratorFactory.createKeyGenerator(algorithm, random);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CRMFException(e.getMessage(), e);
+        }
+    }
+
+    static Object createContentCipher(boolean forEncryption, CipherParameters encKey, AlgorithmIdentifier encryptionAlgID)
+        throws CRMFException
+    {
+        try
+        {
+            return CipherFactory.createContentCipher(forEncryption, encKey, encryptionAlgID);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CRMFException(e.getMessage(), e);
+        }
+    }
+
+    AlgorithmIdentifier generateEncryptionAlgID(ASN1ObjectIdentifier encryptionOID, KeyParameter encKey, SecureRandom random)
+        throws CRMFException
+    {
+        try
+        {
+            return AlgorithmIdentifierFactory.generateEncryptionAlgID(encryptionOID, encKey.getKey().length * 8, random);
+        }
+        catch (IllegalArgumentException e)
+        {
+            throw new CRMFException(e.getMessage(), e);
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
index 91d22a0..31f1b89 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/jcajce/JcaEncryptedValueBuilder.java
@@ -1,15 +1,20 @@
 package org.bouncycastle.cert.crmf.jcajce;
 
+import java.security.PrivateKey;
 import java.security.cert.CertificateEncodingException;
 import java.security.cert.X509Certificate;
 
 import org.bouncycastle.asn1.crmf.EncryptedValue;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.cert.crmf.CRMFException;
 import org.bouncycastle.cert.crmf.EncryptedValueBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.operator.KeyWrapper;
 import org.bouncycastle.operator.OutputEncryptor;
 
+/**
+ * JCA convenience class for EncryptedValueBuilder
+ */
 public class JcaEncryptedValueBuilder
     extends EncryptedValueBuilder
 {
@@ -18,9 +23,30 @@
         super(wrapper, encryptor);
     }
 
+    /**
+     * Build an EncryptedValue structure containing the passed in certificate.
+     *
+     * @param certificate the certificate to be encrypted.
+     * @return an EncryptedValue containing the encrypted certificate.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
     public EncryptedValue build(X509Certificate certificate)
         throws CertificateEncodingException, CRMFException
     {
         return build(new JcaX509CertificateHolder(certificate));
     }
+
+    /**
+     * Build an EncryptedValue structure containing the private key details contained in
+     * the passed PrivateKey.
+     *
+     * @param privateKey the asymmetric private key.
+     * @return an EncryptedValue containing an EncryptedPrivateKeyInfo structure.
+     * @throws CRMFException on a failure to encrypt the data, or wrap the symmetric key for this value.
+     */
+    public EncryptedValue build(PrivateKey privateKey)
+        throws CertificateEncodingException, CRMFException
+    {
+        return build(PrivateKeyInfo.getInstance(privateKey.getEncoded()));
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java
index 2173a92..8c4e2ec 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/crmf/test/AllTests.java
@@ -19,7 +19,6 @@
 import javax.crypto.spec.PSource;
 import javax.security.auth.x500.X500Principal;
 
-import junit.framework.Assert;
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
@@ -34,10 +33,12 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.GeneralName;
+import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.X509v1CertificateBuilder;
 import org.bouncycastle.cert.crmf.CRMFException;
@@ -47,6 +48,8 @@
 import org.bouncycastle.cert.crmf.PKIArchiveControl;
 import org.bouncycastle.cert.crmf.PKMACBuilder;
 import org.bouncycastle.cert.crmf.ValueDecryptorGenerator;
+import org.bouncycastle.cert.crmf.bc.BcCRMFEncryptorBuilder;
+import org.bouncycastle.cert.crmf.bc.BcEncryptedValueBuilder;
 import org.bouncycastle.cert.crmf.bc.BcFixedLengthMGF1Padder;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessage;
 import org.bouncycastle.cert.crmf.jcajce.JcaCertificateRequestMessageBuilder;
@@ -67,9 +70,12 @@
 import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
 import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
 import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.crypto.util.PrivateKeyFactory;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.operator.bc.BcRSAAsymmetricKeyWrapper;
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.operator.jcajce.JceAsymmetricKeyWrapper;
@@ -256,6 +262,58 @@
         TestCase.assertEquals(kp.getPublic(), certReqMsg.getPublicKey());
     }
 
+    public void testEncryptedValueWithKey()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+        
+        JcaEncryptedValueBuilder build = new JcaEncryptedValueBuilder(new JceAsymmetricKeyWrapper(kp.getPublic()).setProvider(BC), new JceCRMFEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        EncryptedValue value = build.build(kp.getPrivate());
+
+        ValueDecryptorGenerator decGen = new JceAsymmetricValueDecryptorGenerator(kp.getPrivate()).setProvider(BC);
+
+        EncryptedValueParser  parser = new EncryptedValueParser(value);
+
+        PrivateKeyInfo privInfo = parser.readPrivateKeyInfo(decGen);
+
+        TestCase.assertEquals(privInfo.getPrivateKeyAlgorithm(), parser.getIntendedAlg());
+
+        TestCase.assertTrue(Arrays.areEqual(privInfo.getEncoded(), kp.getPrivate().getEncoded()));
+    }
+
+    public void testBcEncryptedValueWithKey()
+        throws Exception
+    {
+        KeyPairGenerator kGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        kGen.initialize(512);
+
+        KeyPair kp = kGen.generateKeyPair();
+
+        BcEncryptedValueBuilder build = new BcEncryptedValueBuilder(new BcRSAAsymmetricKeyWrapper(
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE),
+            PublicKeyFactory.createKey(SubjectPublicKeyInfo.getInstance(kp.getPublic().getEncoded()))),
+            new BcCRMFEncryptorBuilder(CMSAlgorithm.AES128_CBC).build());
+
+        EncryptedValue value = build.build(
+            PrivateKeyFactory.createKey(PrivateKeyInfo.getInstance(kp.getPrivate().getEncoded())));
+
+        ValueDecryptorGenerator decGen = new JceAsymmetricValueDecryptorGenerator(kp.getPrivate()).setProvider(BC);
+
+        EncryptedValueParser  parser = new EncryptedValueParser(value);
+
+        PrivateKeyInfo privInfo = parser.readPrivateKeyInfo(decGen);
+
+        TestCase.assertEquals(privInfo.getPrivateKeyAlgorithm(), parser.getIntendedAlg());
+
+        TestCase.assertTrue(Arrays.areEqual(privInfo.getEncoded(), kp.getPrivate().getEncoded()));
+    }
+
     public void testProofOfPossessionWithSender()
         throws Exception
     {
@@ -345,7 +403,7 @@
 
         OutputEncryptor outputEncryptor = encryptorBuilder.build();
 
-        Assert.assertEquals(keySize / 8, ((byte[])(outputEncryptor.getKey().getRepresentation())).length);
+        assertEquals(keySize / 8, ((byte[])(outputEncryptor.getKey().getRepresentation())).length);
     }
 
     public void testEncryptedValue()
@@ -415,9 +473,9 @@
 
         EncryptedValue value = build.build(cert);
 
-        Assert.assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, value.getKeyAlg().getAlgorithm());
-        Assert.assertEquals(NISTObjectIdentifiers.id_sha256, RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getHashAlgorithm().getAlgorithm());
-        Assert.assertEquals(new DEROctetString(new byte[2]), RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getPSourceAlgorithm().getParameters());
+        assertEquals(PKCSObjectIdentifiers.id_RSAES_OAEP, value.getKeyAlg().getAlgorithm());
+        assertEquals(NISTObjectIdentifiers.id_sha256, RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getHashAlgorithm().getAlgorithm());
+        assertEquals(new DEROctetString(new byte[2]), RSAESOAEPparams.getInstance(value.getKeyAlg().getParameters()).getPSourceAlgorithm().getParameters());
 
         ValueDecryptorGenerator decGen = new JceAsymmetricValueDecryptorGenerator(kp.getPrivate()).setProvider(BC);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java
index 507775b..603dc2a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntryFetcherFactory.java
@@ -12,7 +12,6 @@
  *     91d23d115b68072e7a38afeb7e295bd6392a19f25f8328b4ecae4778._smimecert.test.org
  * </pre>
  * In the case of the later ideally just returning a list containing the single entry.
- * </p>
  */
 public interface DANEEntryFetcherFactory
 {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java
index 577ea22..38fa9fb 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/dane/DANEEntrySelectorFactory.java
@@ -22,7 +22,6 @@
      *     new DANEEntrySelectorFactory(new TruncatingDigestCalculator(new SHA256DigestCalculator()));
      * </pre>
      * or some equivalent.
-     * </p>
      *
      * @param digestCalculator a calculator for the message digest to filter email addresses currently truncated SHA-256 (originally SHA-224).
      */
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java
index 0838f08..63c5d57 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/jcajce/JcaX509ExtensionUtils.java
@@ -8,22 +8,34 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.PublicKey;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
 
 import javax.security.auth.x500.X500Principal;
 
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1String;
+import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AuthorityKeyIdentifier;
+import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.GeneralName;
 import org.bouncycastle.asn1.x509.GeneralNames;
 import org.bouncycastle.asn1.x509.SubjectKeyIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509ExtensionUtils;
 import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Integers;
 
 public class JcaX509ExtensionUtils
     extends X509ExtensionUtils
@@ -112,6 +124,73 @@
         return ASN1Primitive.fromByteArray(ASN1OctetString.getInstance(encExtValue).getOctets());
     }
 
+    public static Collection getIssuerAlternativeNames(X509Certificate cert)
+            throws CertificateParsingException
+    {
+        byte[] extVal = cert.getExtensionValue(Extension.issuerAlternativeName.getId());
+
+        return getAlternativeNames(extVal);
+    }
+
+    public static Collection getSubjectAlternativeNames(X509Certificate cert)
+            throws CertificateParsingException
+    {
+        byte[] extVal = cert.getExtensionValue(Extension.subjectAlternativeName.getId());
+
+        return getAlternativeNames(extVal);
+    }
+
+    private static Collection getAlternativeNames(byte[] extVal)
+        throws CertificateParsingException
+    {
+        if (extVal == null)
+        {
+            return Collections.EMPTY_LIST;
+        }
+        try
+        {
+            Collection temp = new ArrayList();
+            Enumeration it = DERSequence.getInstance(parseExtensionValue(extVal)).getObjects();
+            while (it.hasMoreElements())
+            {
+                GeneralName genName = GeneralName.getInstance(it.nextElement());
+                List list = new ArrayList();
+                list.add(Integers.valueOf(genName.getTagNo()));
+                switch (genName.getTagNo())
+                {
+                case GeneralName.ediPartyName:
+                case GeneralName.x400Address:
+                case GeneralName.otherName:
+                    list.add(genName.getName().toASN1Primitive());
+                    break;
+                case GeneralName.directoryName:
+                    list.add(X500Name.getInstance(genName.getName()).toString());
+                    break;
+                case GeneralName.dNSName:
+                case GeneralName.rfc822Name:
+                case GeneralName.uniformResourceIdentifier:
+                    list.add(((ASN1String)genName.getName()).getString());
+                    break;
+                case GeneralName.registeredID:
+                    list.add(ASN1ObjectIdentifier.getInstance(genName.getName()).getId());
+                    break;
+                case GeneralName.iPAddress:
+                    list.add(DEROctetString.getInstance(genName.getName()).getOctets());
+                    break;
+                default:
+                    throw new IOException("Bad tag number: " + genName.getTagNo());
+                }
+
+                temp.add(list);
+            }
+            return Collections.unmodifiableCollection(temp);
+        }
+        catch (Exception e)
+        {
+            throw new CertificateParsingException(e.getMessage());
+        }
+    }
+
     private static class SHA1DigestCalculator
         implements DigestCalculator
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
index f91b3a8..1c1d93a 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPath.java
@@ -43,7 +43,7 @@
     {
         CertPathValidationContext context = new CertPathValidationContext(CertPathUtils.getCriticalExtensionsOIDs(certificates));
 
-        CertPathValidationResultBuilder builder = new CertPathValidationResultBuilder();
+        CertPathValidationResultBuilder builder = new CertPathValidationResultBuilder(context);
 
         for (int i = 0; i != ruleSet.length; i++)
         {
@@ -56,7 +56,7 @@
                 }
                 catch (CertPathValidationException e)
                 {
-                   builder.addException(e);
+                   builder.addException(j, i, e);
                 }
             }
         }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
index facefb4..0a4e618 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResult.java
@@ -3,18 +3,26 @@
 import java.util.Collections;
 import java.util.Set;
 
+import org.bouncycastle.util.Arrays;
+
 public class CertPathValidationResult
 {
     private final boolean isValid;
     private final CertPathValidationException cause;
     private final Set unhandledCriticalExtensionOIDs;
+    private final int certIndex;
+    private final int ruleIndex;
 
+    private CertPathValidationException[] causes;
     private int[] certIndexes;
+    private int[] ruleIndexes;
 
     public CertPathValidationResult(CertPathValidationContext context)
     {
         this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
         this.isValid = this.unhandledCriticalExtensionOIDs.isEmpty();
+        this.certIndex = -1;
+        this.ruleIndex = -1;
         cause = null;
     }
 
@@ -22,16 +30,21 @@
     {
         this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
         this.isValid = false;
+        this.certIndex = certIndex;
+        this.ruleIndex = ruleIndex;
         this.cause = cause;
     }
 
-    public CertPathValidationResult(CertPathValidationContext context, int[] certIndexes, int[] ruleIndexes, CertPathValidationException[] cause)
+    public CertPathValidationResult(CertPathValidationContext context, int[] certIndexes, int[] ruleIndexes, CertPathValidationException[] causes)
     {
-        // TODO
         this.unhandledCriticalExtensionOIDs = Collections.unmodifiableSet(context.getUnhandledCriticalExtensionOIDs());
         this.isValid = false;
-        this.cause = cause[0];
+        this.cause = causes[0];
+        this.certIndex = certIndexes[0];
+        this.ruleIndex = ruleIndexes[0];
+        this.causes = causes;
         this.certIndexes = certIndexes;
+        this.ruleIndexes = ruleIndexes;
     }
 
     public boolean isValid()
@@ -39,7 +52,7 @@
         return isValid;
     }
 
-    public Exception getCause()
+    public CertPathValidationException getCause()
     {
         if (cause != null)
         {
@@ -54,6 +67,16 @@
         return null;
     }
 
+    public int getFailingCertIndex()
+    {
+        return certIndex;
+    }
+
+    public int getFailingRuleIndex()
+    {
+        return ruleIndex;
+    }
+
     public Set getUnhandledCriticalExtensionOIDs()
     {
         return unhandledCriticalExtensionOIDs;
@@ -63,4 +86,34 @@
     {
         return this.certIndexes != null;
     }
+
+    public CertPathValidationException[] getCauses()
+    {
+        if (causes != null)
+        {
+            CertPathValidationException[] rv = new CertPathValidationException[causes.length];
+
+            System.arraycopy(causes, 0, rv, 0, causes.length);
+
+            return rv;
+        }
+
+        if (!unhandledCriticalExtensionOIDs.isEmpty())
+        {
+            return new CertPathValidationException[]
+                { new CertPathValidationException("Unhandled Critical Extensions") };
+        }
+
+        return null;
+    }
+
+    public int[] getFailingCertIndexes()
+    {
+        return Arrays.clone(certIndexes);
+    }
+
+    public int[] getFailingRuleIndexes()
+    {
+        return Arrays.clone(ruleIndexes);
+    }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
index 9e81339..c00d5f7 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/CertPathValidationResultBuilder.java
@@ -1,14 +1,51 @@
 package org.bouncycastle.cert.path;
 
+import java.util.ArrayList;
+import java.util.List;
+
+import org.bouncycastle.util.Integers;
+
 class CertPathValidationResultBuilder
 {
-    public CertPathValidationResult build()
+    private final CertPathValidationContext context;
+    private final List<Integer> certIndexes = new ArrayList<Integer>();
+    private final List<Integer> ruleIndexes = new ArrayList<Integer>();
+    private final List<CertPathValidationException> exceptions = new ArrayList<CertPathValidationException>();
+
+    CertPathValidationResultBuilder(CertPathValidationContext context)
     {
-        return new CertPathValidationResult(null, 0, 0, null);
+        this.context = context;
     }
 
-    public void addException(CertPathValidationException exception)
+    public CertPathValidationResult build()
     {
-        //To change body of created methods use File | Settings | File Templates.
+        if (exceptions.isEmpty())
+        {
+            return new CertPathValidationResult(context);
+        }
+        else
+        {
+            return new CertPathValidationResult(context,
+                toInts(certIndexes), toInts(ruleIndexes), (CertPathValidationException[])exceptions.toArray(new CertPathValidationException[exceptions.size()]));
+        }
+    }
+
+    public void addException(int certIndex, int ruleIndex, CertPathValidationException exception)
+    {
+        this.certIndexes.add(Integers.valueOf(certIndex));
+        this.ruleIndexes.add(Integers.valueOf(ruleIndex));
+        this.exceptions.add(exception);
+    }
+
+    private int[] toInts(List<Integer> values)
+    {
+        int[] rv = new int[values.size()];
+
+        for (int i = 0; i != rv.length; i++)
+        {
+            rv[i] = ((Integer)values.get(i)).intValue();
+        }
+
+        return rv;
     }
 }
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
index 80da034..48cd306 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/path/test/CertPathValidationTest.java
@@ -265,6 +265,13 @@
             fail("basic validation (1) not working");
         }
 
+        result = path.evaluate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier), new BasicConstraintsValidation(), new KeyUsageValidation()});
+
+        if (!result.isValid())
+        {
+            fail("basic evaluation (1) not working");
+        }
+
         List crlList = new ArrayList();
 
         crlList.add(rootCrl);
@@ -305,8 +312,15 @@
             fail("incorrect path validated!!");
         }
 
+        result = path.evaluate(new CertPathValidation[]{new ParentCertIssuedValidation(verifier)});
 
+        if (result.isValid())
+        {
+            fail("incorrect path validated!!");
+        }
 
+        isTrue(result.isDetailed());
+        
 //        List list = new ArrayList();
 //        list.add(rootCert);
 //        list.add(interCert);
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
index f970734..ee73302 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/selector/X509AttributeCertificateHolderSelectorBuilder.java
@@ -157,8 +157,8 @@
      * Adds a collection with target groups criteria. If <code>null</code> is
      * given any will do.
      * <p>
-     * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code representing DER
-     * encoded GeneralNames.
+     * The collection consists of <code>GeneralName</code> objects or <code>byte[]</code>
+     * representing DER encoded GeneralNames.
      *
      * @param names A collection of target groups.
      * @throws java.io.IOException if a parsing error occurs.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java
index 6b85e8e..5f0e9b5 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/AllTests.java
@@ -14,7 +14,7 @@
 {
     public void testSimpleTests()
     {
-        org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertTest(), new DANETest(), new PKCS10Test(), new AttrCertSelectorTest(), new AttrCertTest(), new X509ExtensionUtilsTest(), new CertPathLoopTest() };
+        org.bouncycastle.util.test.Test[] tests = new org.bouncycastle.util.test.Test[] { new CertTest(), new DANETest(), new PKCS10Test(), new AttrCertSelectorTest(), new AttrCertTest(), new X509ExtensionUtilsTest(), new CertPathLoopTest(), new GOST3410_2012CMSTest() };
 
         for (int i = 0; i != tests.length; i++)
         {
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 e47fed7..e289fd0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/BcCertTest.java
@@ -40,6 +40,8 @@
 import org.bouncycastle.asn1.x509.KeyPurposeId;
 import org.bouncycastle.asn1.x509.KeyUsage;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.CertException;
 import org.bouncycastle.cert.X509CRLEntryHolder;
@@ -807,16 +809,9 @@
       */
      public void checkCreation3()
      {
-         ECCurve curve = new ECCurve.Fp(
-             new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-             new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-             new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-         ECDomainParameters params = new ECDomainParameters(
-             curve,
-             curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-             new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-
+         X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+         ECCurve curve = x9.getCurve();
+         ECDomainParameters params = new ECDomainParameters(curve, x9.getG(), x9.getN(), x9.getH());
 
          ECPrivateKeyParameters privKey = new ECPrivateKeyParameters(
              new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java
index 2b0af04..fc2aa23 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/CertTest.java
@@ -1,7 +1,11 @@
 package org.bouncycastle.cert.test;
 
 import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
 import java.io.UnsupportedEncodingException;
 import java.math.BigInteger;
 import java.security.KeyFactory;
@@ -22,7 +26,11 @@
 import java.security.cert.X509CRL;
 import java.security.cert.X509CRLEntry;
 import java.security.cert.X509Certificate;
+import java.security.spec.DSAParameterSpec;
 import java.security.spec.InvalidKeySpecException;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.PSSParameterSpec;
 import java.security.spec.RSAPrivateCrtKeySpec;
 import java.security.spec.RSAPublicKeySpec;
 import java.util.Collection;
@@ -44,6 +52,7 @@
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAPublicKey;
 import org.bouncycastle.asn1.x500.X500Name;
@@ -60,6 +69,8 @@
 import org.bouncycastle.asn1.x509.IssuingDistributionPoint;
 import org.bouncycastle.asn1.x509.KeyPurposeId;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.asn1.x9.ECNamedCurveTable;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLEntryHolder;
@@ -76,6 +87,8 @@
 import org.bouncycastle.cert.jcajce.JcaX509v1CertificateBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509v2CRLBuilder;
 import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
+import org.bouncycastle.crypto.params.DSAParameters;
+import org.bouncycastle.crypto.params.DSAValidationParameters;
 import org.bouncycastle.crypto.params.RSAKeyParameters;
 import org.bouncycastle.crypto.params.RSAPrivateCrtKeyParameters;
 import org.bouncycastle.jce.X509KeyUsage;
@@ -97,7 +110,11 @@
 import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
 import org.bouncycastle.operator.jcajce.JcaContentVerifierProviderBuilder;
 import org.bouncycastle.pqc.jcajce.provider.BouncyCastlePQCProvider;
+import org.bouncycastle.pqc.jcajce.spec.QTESLAParameterSpec;
 import org.bouncycastle.pqc.jcajce.spec.SPHINCS256KeyGenParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSMTParameterSpec;
+import org.bouncycastle.pqc.jcajce.spec.XMSSParameterSpec;
+import org.bouncycastle.util.Encodable;
 import org.bouncycastle.util.Strings;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
@@ -1135,7 +1152,7 @@
             + "RRsRsjse3i2/KClFVd6YLZ+7K1BE0WxFyY2bbytkwQJSxvv3vLSuweFUbhNxutb68wl/yW4GLy4b"
             + "1QdyswNxrNDXTuu5ILKhRDDuWeocz83aG2KGtr3JlFyr3biWGEyn5WUOE6tbONoQDJ0oPYgI6CAc"
             + "EHdUp0lioOCt6UOw7Cs=");
-
+    
     private final byte[] gostRFC4491_94 = Base64.decode(
         "MIICCzCCAboCECMO42BGlSTOxwvklBgufuswCAYGKoUDAgIEMGkxHTAbBgNVBAMM" +
             "FEdvc3RSMzQxMC05NCBleGFtcGxlMRIwEAYDVQQKDAlDcnlwdG9Qcm8xCzAJBgNV" +
@@ -1163,28 +1180,28 @@
 
     private final byte[] sha3Cert = Base64.decode(
         "MIID8jCCAqagAwIBAgIICfBykpzUT+IwQQYJKoZIhvcNAQEKMDSgDzANBglg"
-      + "hkgBZQMEAggFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEg"
-      + "MCwxCzAJBgNVBAYTAkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNB"
-      + "MTAeFw0xNjEwMTgxODQzMjhaFw0yNjEwMTgxODQzMjdaMCwxCzAJBgNVBAYT"
-      + "AkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNBMTCCASIwDQYJKoZI"
-      + "hvcNAQEBBQADggEPADCCAQoCggEBAK/pzm1RASDYDg3WBXyW3AnAESRF/+li"
-      + "qh0X8Y89m+JFJeOi1u89bOSPjsFfo5SbRSElyRXedh/d37KrONg39NEKIcC6"
-      + "iSuiNfXu0D6nlSzhrQzmvHIyfLnm8N2JtHDr/hZIprOcFO+lZTJIjjrOVe9y"
-      + "lFGgGDd/uQCEJk1Cmi5Ivi9odeiN3z8lVlGNeN9/Q5n47ijuYWr73z/FyyAK"
-      + "gAG3B5nhAYWs4ft0O3JWBc0QJZzShqsRjm3SNhAqMDnRoTq04PFgbDYizV8T"
-      + "ydz2kCne79TDwsY4MckYYaGoNcPoQXVS+9YjQjI72ktSlxiJxodL9WMFl+ED"
-      + "5ZLBRIRsDJECAwEAAaOBrzCBrDAPBgNVHRMBAf8EBTADAQH/MGoGCCsGAQUF"
-      + "BwEBBF4wXDAnBggrBgEFBQcwAoYbaHR0cDovL2V4YW1wbGUub3JnL1JDQTEu"
-      + "ZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vbG9jYWxob3N0OjgwODAvb2NzcC9y"
-      + "ZXNwb25kZXIxMB0GA1UdDgQWBBRTXKdJI3P1kveLlRxPvzUfDnC8JjAOBgNV"
-      + "HQ8BAf8EBAMCAQYwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAggFAKEc"
-      + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEgA4IBAQCpSVaqOMKz"
-      + "6NT0+mivEhig9cKsglFhnWStKUtdhrG4HqOf6Qjny9Xvq1nE7x8e2xAoaZLd"
-      + "GMsNAWFCbwzoJrDL7Ct6itQ5ymxi2haN+Urc5UWJd/8C0R74OdP1uPCiljZ9"
-      + "DdjbNk/hS36UPYi+FT5r6Jr/1X/EqgL1MOUsSTEXdYlZH662zjbV4D9QSBzx"
-      + "ul9bYyWrqSZFKvKef4UQwUy8yXtChwiwp50mfJQBdVcIqPBYCgmLYclamjQx"
-      + "hlkk5VbZb4D/Cv4HxrdxpJfy/ewUZR7uHlzDx0/m4qjzNzWgq+sh3ZbveDrV"
-      + "wd/FDMFOxSIno9qgHtdfgXRwZJ+l07fF");
+            + "hkgBZQMEAggFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEg"
+            + "MCwxCzAJBgNVBAYTAkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNB"
+            + "MTAeFw0xNjEwMTgxODQzMjhaFw0yNjEwMTgxODQzMjdaMCwxCzAJBgNVBAYT"
+            + "AkRFMQ4wDAYDVQQKDAV4aXBraTENMAsGA1UEAwwEUkNBMTCCASIwDQYJKoZI"
+            + "hvcNAQEBBQADggEPADCCAQoCggEBAK/pzm1RASDYDg3WBXyW3AnAESRF/+li"
+            + "qh0X8Y89m+JFJeOi1u89bOSPjsFfo5SbRSElyRXedh/d37KrONg39NEKIcC6"
+            + "iSuiNfXu0D6nlSzhrQzmvHIyfLnm8N2JtHDr/hZIprOcFO+lZTJIjjrOVe9y"
+            + "lFGgGDd/uQCEJk1Cmi5Ivi9odeiN3z8lVlGNeN9/Q5n47ijuYWr73z/FyyAK"
+            + "gAG3B5nhAYWs4ft0O3JWBc0QJZzShqsRjm3SNhAqMDnRoTq04PFgbDYizV8T"
+            + "ydz2kCne79TDwsY4MckYYaGoNcPoQXVS+9YjQjI72ktSlxiJxodL9WMFl+ED"
+            + "5ZLBRIRsDJECAwEAAaOBrzCBrDAPBgNVHRMBAf8EBTADAQH/MGoGCCsGAQUF"
+            + "BwEBBF4wXDAnBggrBgEFBQcwAoYbaHR0cDovL2V4YW1wbGUub3JnL1JDQTEu"
+            + "ZGVyMDEGCCsGAQUFBzABhiVodHRwOi8vbG9jYWxob3N0OjgwODAvb2NzcC9y"
+            + "ZXNwb25kZXIxMB0GA1UdDgQWBBRTXKdJI3P1kveLlRxPvzUfDnC8JjAOBgNV"
+            + "HQ8BAf8EBAMCAQYwQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAggFAKEc"
+            + "MBoGCSqGSIb3DQEBCDANBglghkgBZQMEAggFAKIDAgEgA4IBAQCpSVaqOMKz"
+            + "6NT0+mivEhig9cKsglFhnWStKUtdhrG4HqOf6Qjny9Xvq1nE7x8e2xAoaZLd"
+            + "GMsNAWFCbwzoJrDL7Ct6itQ5ymxi2haN+Urc5UWJd/8C0R74OdP1uPCiljZ9"
+            + "DdjbNk/hS36UPYi+FT5r6Jr/1X/EqgL1MOUsSTEXdYlZH662zjbV4D9QSBzx"
+            + "ul9bYyWrqSZFKvKef4UQwUy8yXtChwiwp50mfJQBdVcIqPBYCgmLYclamjQx"
+            + "hlkk5VbZb4D/Cv4HxrdxpJfy/ewUZR7uHlzDx0/m4qjzNzWgq+sh3ZbveDrV"
+            + "wd/FDMFOxSIno9qgHtdfgXRwZJ+l07fF");
 
     private static byte[] sm_root = Base64.decode(
         "MIICwzCCAmmgAwIBAgIIIBQGIgAAAAMwCgYIKoEcz1UBg3UwgdgxCzAJBgNVBAYT" +
@@ -1205,27 +1222,73 @@
 
     private static byte[] sm_sign = Base64.decode(
         "MIID9zCCA5ygAwIBAgIIIBcEJwKSCCMwCgYIKoEcz1UBg3UwgccxCzAJBgNVBAYT" +
-        "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
-        "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
-        "5Lu75YWs5Y+4MTwwOgYDVQQLDDPmsZ/oi4/nnIHnlLXlrZDllYbliqHmnI3liqHk" +
-        "uK3lv4PmnInpmZDotKPku7vlhazlj7gxFDASBgNVBAMMC0pTQ0FfQ0FfU00yMB4X" +
-        "DTE3MDQyNzAwMzkwNVoXDTE4MDQyNzAwMzkwNVowggEdMQ4wDAYDVQRYDAUwMDAw" +
-        "MTESMBAGA1UEGgwJ5biC6L6W5Yy6MRswGQYDVQQBDBIzMjAxMTIxOTgxMDUxMTAw" +
-        "MTQxDTALBgRVBIhYDAM0NTYxDTALBgRVBIhXDAMxMjMxEjAQBgNVBC0MCXVzZXJD" +
-        "ZXJ0MjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCeaxn+iLj+ecgTESMBAGA1UEBwwJ" +
-        "5Y2X5Lqs5biCMQwwCgYDVQQLDAMwMDgxHzAdBgkqhkiG9w0BCQEWEDMyNzMyMTU2" +
-        "OEBxcS5jb20xITAfBgNVBCoMGOa1i+ivlee9keWFs1NNMueul+azlTEyMzEhMB8G" +
-        "A1UEAwwY5rWL6K+V572R5YWzU00y566X5rOVMTIzMFkwEwYHKoZIzj0CAQYIKoEc" +
-        "z1UBgi0DQgAEdbrBzy2y8Gz4grOF23iaDipPhRPQRApAMIAP0cAuL1tATFjFuWJs" +
-        "pBc1cnCZmsOJnVpV4W7VF8hNOaqv3Tq4NqOCARcwggETMAkGA1UdEwQCMAAwCwYD" +
-        "VR0PBAQDAgbAMB0GA1UdDgQWBBRsWSOQDniy75t7UEvTXugwfq0HpzAfBgNVHSME" +
-        "GDAWgBT/02hyCI7lesT55ixTRU6RpLF6AzAxBgNVHSUEKjAoBggrBgEFBQcDAgYI" +
-        "KwYBBQUHAwgGCCsGAQUFBwMEBggrBgEFBQcDCDA9BgNVHR8ENjA0MDKgMKAuhixo" +
-        "dHRwOi8vY3JsLmpzY2EuY29tLmNuL2NybC9TTTJDUkxfRU5USVRZLmNybDBHBggr" +
-        "BgEFBQcBAQQ7MDkwNwYIKwYBBQUHMAKBK2h0dHA6Ly8xMC4xMDguNS4yOjg4ODAv" +
-        "ZG93bmxvYWQvSlNDQV9DQS5jZXIwCgYIKoEcz1UBg3UDSQAwRgIhALFoMoA1+uO4" +
-        "tXfmoyePz1pmv0CWPBgEP1EfDeS6FPitAiEAjHJYq7ryHKULqpRg6ph9r+xUDoWd" +
-        "0TPMOQ9jj4XJPO4=");
+            "AkNOMRIwEAYDVQQIDAnmsZ/oi4/nnIExEjAQBgNVBAcMCeWNl+S6rOW4gjE8MDoG" +
+            "A1UECgwz5rGf6IuP55yB55S15a2Q5ZWG5Yqh5pyN5Yqh5Lit5b+D5pyJ6ZmQ6LSj" +
+            "5Lu75YWs5Y+4MTwwOgYDVQQLDDPmsZ/oi4/nnIHnlLXlrZDllYbliqHmnI3liqHk" +
+            "uK3lv4PmnInpmZDotKPku7vlhazlj7gxFDASBgNVBAMMC0pTQ0FfQ0FfU00yMB4X" +
+            "DTE3MDQyNzAwMzkwNVoXDTE4MDQyNzAwMzkwNVowggEdMQ4wDAYDVQRYDAUwMDAw" +
+            "MTESMBAGA1UEGgwJ5biC6L6W5Yy6MRswGQYDVQQBDBIzMjAxMTIxOTgxMDUxMTAw" +
+            "MTQxDTALBgRVBIhYDAM0NTYxDTALBgRVBIhXDAMxMjMxEjAQBgNVBC0MCXVzZXJD" +
+            "ZXJ0MjELMAkGA1UEBhMCQ04xEjAQBgNVBAgMCeaxn+iLj+ecgTESMBAGA1UEBwwJ" +
+            "5Y2X5Lqs5biCMQwwCgYDVQQLDAMwMDgxHzAdBgkqhkiG9w0BCQEWEDMyNzMyMTU2" +
+            "OEBxcS5jb20xITAfBgNVBCoMGOa1i+ivlee9keWFs1NNMueul+azlTEyMzEhMB8G" +
+            "A1UEAwwY5rWL6K+V572R5YWzU00y566X5rOVMTIzMFkwEwYHKoZIzj0CAQYIKoEc" +
+            "z1UBgi0DQgAEdbrBzy2y8Gz4grOF23iaDipPhRPQRApAMIAP0cAuL1tATFjFuWJs" +
+            "pBc1cnCZmsOJnVpV4W7VF8hNOaqv3Tq4NqOCARcwggETMAkGA1UdEwQCMAAwCwYD" +
+            "VR0PBAQDAgbAMB0GA1UdDgQWBBRsWSOQDniy75t7UEvTXugwfq0HpzAfBgNVHSME" +
+            "GDAWgBT/02hyCI7lesT55ixTRU6RpLF6AzAxBgNVHSUEKjAoBggrBgEFBQcDAgYI" +
+            "KwYBBQUHAwgGCCsGAQUFBwMEBggrBgEFBQcDCDA9BgNVHR8ENjA0MDKgMKAuhixo" +
+            "dHRwOi8vY3JsLmpzY2EuY29tLmNuL2NybC9TTTJDUkxfRU5USVRZLmNybDBHBggr" +
+            "BgEFBQcBAQQ7MDkwNwYIKwYBBQUHMAKBK2h0dHA6Ly8xMC4xMDguNS4yOjg4ODAv" +
+            "ZG93bmxvYWQvSlNDQV9DQS5jZXIwCgYIKoEcz1UBg3UDSQAwRgIhALFoMoA1+uO4" +
+            "tXfmoyePz1pmv0CWPBgEP1EfDeS6FPitAiEAjHJYq7ryHKULqpRg6ph9r+xUDoWd" +
+            "0TPMOQ9jj4XJPO4=");
+
+    private static byte[] gost_2012_cert = Base64.decode(
+        "MIIEfDCCBCmgAwIBAgIECiew2zAKBggqhQMHAQEDAjCB8TELMAkGA1UEBhMCUlUxKjAoBgNVBAgMIdCh0LDQvdC60YLRii3Q" +
+            "n9C10YLQtdGA0LHRg9GA0LPRijEuMCwGA1UECgwl0JbRg9GA0L3QsNC7ICLQodC+0LLRgNC10LzQtdC90L3QuNC6IjEfMB0G" +
+            "A1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEoMCYGA1UEDAwf0JPQu9Cw0LLQvdGL0Lkg0YDQtdC00LDQutGC0L7RgDE7" +
+            "MDkGA1UEAwwy0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhyDQn9GD0YjQutC40L0wHhcNMTcwNzAyMTQw" +
+            "MDAwWhcNMzcwNzAyMTQwMDAwWjCB8TELMAkGA1UEBhMCUlUxKjAoBgNVBAgMIdCh0LDQvdC60YLRii3Qn9C10YLQtdGA0LHR" +
+            "g9GA0LPRijEuMCwGA1UECgwl0JbRg9GA0L3QsNC7ICLQodC+0LLRgNC10LzQtdC90L3QuNC6IjEfMB0GA1UECwwW0KDRg9C6" +
+            "0L7QstC+0LTRgdGC0LLQvjEoMCYGA1UEDAwf0JPQu9Cw0LLQvdGL0Lkg0YDQtdC00LDQutGC0L7RgDE7MDkGA1UEAwwy0JDQ" +
+            "u9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhyDQn9GD0YjQutC40L0wZjAfBggqhQMHAQEBATATBgcqhQMCAiQA" +
+            "BggqhQMHAQECAgNDAARA6UpRcgr4pVAKuYkEQ0XKicUxjhd8jbCEz3OFYQ/wSQnuXR5RquASztlnnmnb5W/PKEAnElAUxW0j" +
+            "ROLOGZrDWaOCAZ4wggGaMA4GA1UdDwEB/wQEAwIB/jAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUFBwMD" +
+            "BggrBgEFBQcDBDAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBQLWqbXBTDJanrRowAoQbAaXbKRDTCCASMGA1UdIwSCARow" +
+            "ggEWgBQLWqbXBTDJanrRowAoQbAaXbKRDaGB96SB9DCB8TELMAkGA1UEBhMCUlUxKjAoBgNVBAgMIdCh0LDQvdC60YLRii3Q" +
+            "n9C10YLQtdGA0LHRg9GA0LPRijEuMCwGA1UECgwl0JbRg9GA0L3QsNC7ICLQodC+0LLRgNC10LzQtdC90L3QuNC6IjEfMB0G" +
+            "A1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEoMCYGA1UEDAwf0JPQu9Cw0LLQvdGL0Lkg0YDQtdC00LDQutGC0L7RgDE7" +
+            "MDkGA1UEAwwy0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhyDQn9GD0YjQutC40L2CBAonsNswCgYIKoUD" +
+            "BwEBAwIDQQDL2un6Wxn0frStAheZsd34ANDFwAb0rCOInrXsi6HOAxgIuS+9iICiQNTRlQ6x9LSWOUf+aa7kDDU5P4Ovd5od");
+
+    private static byte[] gost_2012_privateKey = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQg0MVlKYHb5/AwO1ZjNW8nhjyX3IgHo7nPSKuvKf87" +
+            "tTU=");
+
+    private static DSAParameters def2048Params = new DSAParameters(
+        new BigInteger("95475cf5d93e596c3fcd1d902add02f427f5f3c7210313bb45fb4d5b" +
+                        "b2e5fe1cbd678cd4bbdd84c9836be1f31c0777725aeb6c2fc38b85f4" +
+                        "8076fa76bcd8146cc89a6fb2f706dd719898c2083dc8d896f84062e2" +
+                        "c9c94d137b054a8d8096adb8d51952398eeca852a0af12df83e475aa" +
+                        "65d4ec0c38a9560d5661186ff98b9fc9eb60eee8b030376b236bc73b" +
+                        "e3acdbd74fd61c1d2475fa3077b8f080467881ff7e1ca56fee066d79" +
+                        "506ade51edbb5443a563927dbc4ba520086746175c8885925ebc64c6" +
+                        "147906773496990cb714ec667304e261faee33b3cbdf008e0c3fa906" +
+                        "50d97d3909c9275bf4ac86ffcb3d03e6dfc8ada5934242dd6d3bcca2" +
+                        "a406cb0b", 16),
+        new BigInteger("f8183668ba5fc5bb06b5981e6d8b795d30b8978d43ca0ec572e37e09939a9773", 16),
+        new BigInteger("42debb9da5b3d88cc956e08787ec3f3a09bba5f48b889a74aaf53174" +
+                        "aa0fbe7e3c5b8fcd7a53bef563b0e98560328960a9517f4014d3325f" +
+                        "c7962bf1e049370d76d1314a76137e792f3f0db859d095e4a5b93202" +
+                        "4f079ecf2ef09c797452b0770e1350782ed57ddf794979dcef23cb96" +
+                        "f183061965c4ebc93c9c71c56b925955a75f94cccf1449ac43d586d0" +
+                        "beee43251b0b2287349d68de0d144403f13e802f4146d882e057af19" +
+                        "b6f6275c6676c8fa0e3ca2713a3257fd1b27d0639f695e347d8d1cf9" +
+                        "ac819a26ca9b04cb0eb9b7b035988d15bbac65212a55239cfc7e58fa" +
+                        "e38d7250ab9991ffbc97134025fe8ce04c4399ad96569be91a546f49" +
+                        "78693c7a", 16),
+        new DSAValidationParameters(Hex.decode("b0b4417601b59cbc9d8ac8f935cadaec4f5fbb2f23785609ae466748d9b5a536"), 497));
 
     private PublicKey dudPublicKey = new PublicKey()
     {
@@ -1267,7 +1330,8 @@
             Certificate cert = fact.generateCertificate(bIn);
 
             PublicKey k = cert.getPublicKey();
-            // System.out.println(cert);
+//            System.out.println("****** " + id + " ******");
+//            System.out.println(cert);
         }
         catch (Exception e)
         {
@@ -1356,7 +1420,7 @@
 
             X509CertificateHolder certHldr = new X509CertificateHolder(bytes);
 
-            certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(k));
+            isTrue(certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(k)));
             // System.out.println(cert);
         }
         catch (Exception e)
@@ -1366,6 +1430,53 @@
 
     }
 
+    public void checkSelfSignedCertificateAndKey(
+        int id,
+        byte[] certBytes,
+        String sigAlgorithm,
+        byte[] keyBytes)
+    {
+        ByteArrayInputStream bIn;
+        String dump = "";
+
+        try
+        {
+            bIn = new ByteArrayInputStream(certBytes);
+
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", "BC");
+
+            Certificate cert = fact.generateCertificate(bIn);
+
+            PublicKey k = cert.getPublicKey();
+
+            X509CertificateHolder certHldr = new X509CertificateHolder(certBytes);
+
+            isTrue(certHldr.isSignatureValid(new JcaContentVerifierProviderBuilder().setProvider("BC").build(k)));
+            // System.out.println(cert);
+
+            KeyFactory keyFactory = KeyFactory.getInstance(k.getAlgorithm(), "BC");
+
+            PrivateKey privKey = keyFactory.generatePrivate(new PKCS8EncodedKeySpec(keyBytes));
+
+            Signature signer = Signature.getInstance(sigAlgorithm, "BC");
+
+            signer.initSign(privKey);
+
+            signer.update(certBytes);
+
+            byte[] sig = signer.sign();
+
+            signer.initVerify(cert);
+
+            signer.update(certBytes);
+
+            isTrue(signer.verify(sig));
+        }
+        catch (Exception e)
+        {
+            fail(dump + Strings.lineSeparator() + getName() + ": " + id + " failed - exception " + e.toString(), e);
+        }
+    }
 
     /**
      * Test a generated certificate with the sun provider
@@ -1378,7 +1489,7 @@
         certFact.generateCertificate(new ByteArrayInputStream(encoding));
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - RSA
      */
     public void checkCreation1()
@@ -1604,7 +1715,7 @@
         }
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - DSA
      */
     public void checkCreation2()
@@ -1699,8 +1810,8 @@
         }
     }
 
-    /**
-     * we generate a self signed certificate for the sake of testing - DSA
+    /*
+     * we generate a self signed certificate for the sake of testing - SM3withSM2
      */
     public void checkSm3WithSm2Creation()
         throws Exception
@@ -1762,7 +1873,7 @@
 
         cert = (X509Certificate)fact.generateCertificate(bIn);
 
-        // System.out.println(cert);
+        cert.getEncoded();
     }
 
     private void checkComparison(byte[] encCert)
@@ -1798,21 +1909,14 @@
         return builder;
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - ECDSA
      */
     public void checkCreation3()
     {
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
-
+        X9ECParameters x9 = ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("876300101507107567501066130761671078357010671067781776716671676178726717"), // d
@@ -1928,21 +2032,15 @@
 
     }
 
-    /**
+    /*
      * we generate a self signed certificate for the sake of testing - SHA224withECDSA
      */
     private void createECCert(String algorithm, ASN1ObjectIdentifier algOid)
         throws Exception
     {
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), // q (or p)
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16));  // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")), // G
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16)); // n
+        X9ECParameters x9 = ECNamedCurveTable.getByName("secp521r1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("5769183828869504557786041598510887460263120754767955773309066354712783118202294874205844512909370791582896372147797293913785865682804434049019366394746072023"), // d
@@ -2155,6 +2253,8 @@
 
         X509CRL crl = new JcaX509CRLConverter().setProvider(BC).getCRL(crlHolder);
 
+        crl.verify(pair.getPublic(), "BC");
+
         if (!crl.getIssuerX500Principal().equals(new X500Principal("CN=Test CA")))
         {
             fail("failed CRL issuer test");
@@ -2356,6 +2456,8 @@
 
         X509CRL readCrl = (X509CRL)cFact.generateCRL(new ByteArrayInputStream(crlHolder.getEncoded()));
 
+        readCrl.verify(pair.getPublic(), "BC");
+
         if (readCrl == null)
         {
             fail("crl not returned!");
@@ -2369,7 +2471,187 @@
         }
     }
 
-    /**
+    public void checkCRLCreation4()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        Date now = new Date();
+        KeyPair pair = kpGen.generateKeyPair();
+        X509v2CRLBuilder crlGen = new JcaX509v2CRLBuilder(new X500Principal("CN=Test CA"), now);
+
+        crlGen.setNextUpdate(new Date(now.getTime() + 100000));
+
+        Vector extOids = new Vector();
+        Vector extValues = new Vector();
+
+        CRLReason crlReason = CRLReason.lookup(CRLReason.privilegeWithdrawn);
+
+        try
+        {
+            extOids.addElement(Extension.reasonCode);
+            extValues.addElement(new Extension(Extension.reasonCode, false, new DEROctetString(crlReason.getEncoded())));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("error encoding reason: " + e);
+        }
+
+        Extensions entryExtensions = generateExtensions(extOids, extValues);
+
+        crlGen.addCRLEntry(BigInteger.ONE, now, entryExtensions);
+
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(pair.getPublic()));
+
+        X509CRLHolder crlHolder = crlGen.build(new JcaContentSignerBuilder("SHA256withRSAandMGF1").setProvider(BC).build(pair.getPrivate()));
+
+        X509CRL crl = new JcaX509CRLConverter().setProvider(BC).getCRL(crlHolder);
+
+        crl.verify(pair.getPublic(), "BC");
+
+        if (!crl.getIssuerX500Principal().equals(new X500Principal("CN=Test CA")))
+        {
+            fail("failed CRL issuer test");
+        }
+
+        byte[] authExt = crl.getExtensionValue(Extension.authorityKeyIdentifier.getId());
+
+        if (authExt == null)
+        {
+            fail("failed to find CRL extension");
+        }
+
+        AuthorityKeyIdentifier authId = AuthorityKeyIdentifier.getInstance(ASN1OctetString.getInstance(authExt).getOctets());
+
+        X509CRLEntry entry = crl.getRevokedCertificate(BigInteger.ONE);
+
+        if (entry == null)
+        {
+            fail("failed to find CRL entry");
+        }
+
+        if (!entry.getSerialNumber().equals(BigInteger.ONE))
+        {
+            fail("CRL cert serial number does not match");
+        }
+
+        if (!entry.hasExtensions())
+        {
+            fail("CRL entry extension not found");
+        }
+
+        byte[] ext = entry.getExtensionValue(Extension.reasonCode.getId());
+
+        if (ext != null)
+        {
+            ASN1Enumerated reasonCode = (ASN1Enumerated)fromExtensionValue(ext);
+
+            if (reasonCode.getValue().intValue() != CRLReason.privilegeWithdrawn)
+            {
+                fail("CRL entry reasonCode wrong");
+            }
+        }
+        else
+        {
+            fail("CRL entry reasonCode not found");
+        }
+    }
+
+    public void checkCRLCreation5()
+        throws Exception
+    {
+        KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA", BC);
+
+        Date now = new Date();
+        KeyPair pair = kpGen.generateKeyPair();
+        X509v2CRLBuilder crlGen = new JcaX509v2CRLBuilder(new X500Principal("CN=Test CA"), now);
+
+        crlGen.setNextUpdate(new Date(now.getTime() + 100000));
+
+        Vector extOids = new Vector();
+        Vector extValues = new Vector();
+
+        CRLReason crlReason = CRLReason.lookup(CRLReason.privilegeWithdrawn);
+
+        try
+        {
+            extOids.addElement(Extension.reasonCode);
+            extValues.addElement(new Extension(Extension.reasonCode, false, new DEROctetString(crlReason.getEncoded())));
+        }
+        catch (IOException e)
+        {
+            throw new IllegalArgumentException("error encoding reason: " + e);
+        }
+
+        Extensions entryExtensions = generateExtensions(extOids, extValues);
+
+        crlGen.addCRLEntry(BigInteger.ONE, now, entryExtensions);
+
+        JcaX509ExtensionUtils extUtils = new JcaX509ExtensionUtils();
+
+        crlGen.addExtension(Extension.authorityKeyIdentifier, false, extUtils.createAuthorityKeyIdentifier(pair.getPublic()));
+
+        ContentSigner signer = new JcaContentSignerBuilder(
+            "RSAPSS",
+            new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 20, 1))
+                                        .setProvider(BC).build(pair.getPrivate());
+        X509CRLHolder crlHolder = crlGen.build(signer);
+
+        X509CRL crl = new JcaX509CRLConverter().setProvider(BC).getCRL(crlHolder);
+
+        crl.verify(pair.getPublic(), "BC");
+
+        if (!crl.getIssuerX500Principal().equals(new X500Principal("CN=Test CA")))
+        {
+            fail("failed CRL issuer test");
+        }
+
+        byte[] authExt = crl.getExtensionValue(Extension.authorityKeyIdentifier.getId());
+
+        if (authExt == null)
+        {
+            fail("failed to find CRL extension");
+        }
+
+        AuthorityKeyIdentifier authId = AuthorityKeyIdentifier.getInstance(ASN1OctetString.getInstance(authExt).getOctets());
+
+        X509CRLEntry entry = crl.getRevokedCertificate(BigInteger.ONE);
+
+        if (entry == null)
+        {
+            fail("failed to find CRL entry");
+        }
+
+        if (!entry.getSerialNumber().equals(BigInteger.ONE))
+        {
+            fail("CRL cert serial number does not match");
+        }
+
+        if (!entry.hasExtensions())
+        {
+            fail("CRL entry extension not found");
+        }
+
+        byte[] ext = entry.getExtensionValue(Extension.reasonCode.getId());
+
+        if (ext != null)
+        {
+            ASN1Enumerated reasonCode = (ASN1Enumerated)fromExtensionValue(ext);
+
+            if (reasonCode.getValue().intValue() != CRLReason.privilegeWithdrawn)
+            {
+                fail("CRL entry reasonCode wrong");
+            }
+        }
+        else
+        {
+            fail("CRL entry reasonCode not found");
+        }
+    }
+
+    /*
      * we generate a self signed certificate for the sake of testing - GOST3410
      */
     public void checkCreation4()
@@ -2576,7 +2858,10 @@
 
         cert.checkValidity(new Date());
 
-        cert.verify(pubKey, "BCPQC");
+        cert.verify(cert.getPublicKey());
+
+        // check encoded works
+        cert.getEncoded();
 
         if (!areEqual(baseCert.getExtensionValue("2.5.29.15"), cert.getExtensionValue("2.5.29.15")))
         {
@@ -2602,6 +2887,487 @@
         {
             // expected
         }
+
+        // certificate with NewHope key
+        kpGen = KeyPairGenerator.getInstance("NH", "BCPQC");
+
+        kpGen.initialize(1024, new SecureRandom());
+
+        KeyPair nhKp = kpGen.generateKeyPair();
+
+        certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), nhKp.getPublic())
+            .copyAndAddExtension(new ASN1ObjectIdentifier("2.5.29.15"), true, baseCert)
+            .copyAndAddExtension(new ASN1ObjectIdentifier("2.5.29.37"), false, baseCert);
+
+        cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        cert.verify(pubKey);
+
+        isTrue(nhKp.getPublic().equals(cert.getPublicKey()));
+
+        // check encoded works
+        cert.getEncoded();
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - ECGOST3410-2012
+     */
+    public void checkCreation7()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator g = KeyPairGenerator.getInstance("ECGOST3410-2012", BC);
+
+        g.initialize(new ECNamedCurveGenParameterSpec("Tc26-Gost-3410-12-512-paramSetA"), new SecureRandom());
+
+        KeyPair p = g.generateKeyPair();
+
+        PrivateKey privKey = p.getPrivate();
+        PublicKey pubKey = p.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("GOST3411-2012-512WITHECGOST3410-2012-512").setProvider(BC).build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - XMSS
+     */
+    public void checkCreation8()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSS", "BCPQC");
+
+        kpg.initialize(new XMSSParameterSpec(10, XMSSParameterSpec.SHA256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("SHA256withXMSS").setProvider("BCPQC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - XMSS^MT
+     */
+    public void checkCreation9()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("XMSSMT", "BCPQC");
+
+        kpg.initialize(new XMSSMTParameterSpec(10, 5, XMSSMTParameterSpec.SHAKE256), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("SHAKE256withXMSSMT").setProvider("BCPQC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate for the sake of testing - qTESLA
+     */
+    public void checkCreationQTESLA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("qTESLA", "BCPQC");
+
+        kpg.initialize(new QTESLAParameterSpec(QTESLAParameterSpec.HEURISTIC_I), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder(pubKey.getAlgorithm()).setProvider("BCPQC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate across the range of DSA algorithms
+     */
+    public void checkCreationDSA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA", "BC");
+
+        kpg.initialize(new DSAParameterSpec(def2048Params.getP(), def2048Params.getQ(), def2048Params.getG()), new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        String[] algs = new String[]
+            {
+                "SHA1WITHDSA",
+                "DSAWITHSHA1",
+                "SHA224WITHDSA",
+                "SHA256WITHDSA",
+                "SHA384WITHDSA",
+                "SHA512WITHDSA",
+                "SHA3-224WITHDSA",
+                "SHA3-256WITHDSA",
+                "SHA3-384WITHDSA",
+                "SHA3-512WITHDSA"
+            };
+
+        ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[]
+            {
+                X9ObjectIdentifiers.id_dsa_with_sha1,
+                X9ObjectIdentifiers.id_dsa_with_sha1,
+                NISTObjectIdentifiers.dsa_with_sha224,
+                NISTObjectIdentifiers.dsa_with_sha256,
+                NISTObjectIdentifiers.dsa_with_sha384,
+                NISTObjectIdentifiers.dsa_with_sha512,
+                NISTObjectIdentifiers.id_dsa_with_sha3_224,
+                NISTObjectIdentifiers.id_dsa_with_sha3_256,
+                NISTObjectIdentifiers.id_dsa_with_sha3_384,
+                NISTObjectIdentifiers.id_dsa_with_sha3_512
+            };
+
+        doGenSelfSignedCert(privKey, pubKey, algs, oids);
+    }
+
+    /*
+     * we generate a self signed certificate across the range of RSA algorithms
+     */
+    public void checkCreationRSA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", "BC");
+
+        kpg.initialize(2048, new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        String[] algs = new String[]
+            {
+                "SHA1WITHRSA",
+                "SHA1WITHRSAENCRYPTION",
+                "SHA224WITHRSA",
+                "SHA224WITHRSAENCRYPTION",
+                "SHA256WITHRSA",
+                "SHA256WITHRSAENCRYPTION",
+                "SHA384WITHRSA",
+                "SHA384WITHRSAENCRYPTION",
+                "SHA512WITHRSA",
+                "SHA512WITHRSAENCRYPTION",
+                "SHA3-224WITHRSA",
+                "SHA3-224WITHRSAENCRYPTION",
+                "SHA3-256WITHRSA",
+                "SHA3-256WITHRSAENCRYPTION",
+                "SHA3-384WITHRSA",
+                "SHA3-384WITHRSAENCRYPTION",
+                "SHA3-512WITHRSA",
+                "SHA3-512WITHRSAENCRYPTION",
+                "SHA1WITHRSAANDMGF1",
+                "SHA224WITHRSAANDMGF1",
+                "SHA256WITHRSAANDMGF1",
+                "SHA384WITHRSAANDMGF1",
+                "SHA512WITHRSAANDMGF1",
+                "SHA3-224WITHRSAANDMGF1",
+                "SHA3-256WITHRSAANDMGF1",
+                "SHA3-384WITHRSAANDMGF1",
+                "SHA3-512WITHRSAANDMGF1",
+            };
+
+        ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[]
+            {
+                PKCSObjectIdentifiers.sha1WithRSAEncryption,
+                PKCSObjectIdentifiers.sha1WithRSAEncryption,
+                PKCSObjectIdentifiers.sha224WithRSAEncryption,
+                PKCSObjectIdentifiers.sha224WithRSAEncryption,
+                PKCSObjectIdentifiers.sha256WithRSAEncryption,
+                PKCSObjectIdentifiers.sha256WithRSAEncryption,
+                PKCSObjectIdentifiers.sha384WithRSAEncryption,
+                PKCSObjectIdentifiers.sha384WithRSAEncryption,
+                PKCSObjectIdentifiers.sha512WithRSAEncryption,
+                PKCSObjectIdentifiers.sha512WithRSAEncryption,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512,
+                NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS,
+                PKCSObjectIdentifiers.id_RSASSA_PSS
+            };
+
+        doGenSelfSignedCert(privKey, pubKey, algs, oids);
+
+        X500NameBuilder builder = createStdBuilder();
+
+        //
+        // create the certificate - version 3
+        //
+        ContentSigner sigGen = new JcaContentSignerBuilder("RSAPSS",
+            new PSSParameterSpec("SHA-256", "MGF1", new MGF1ParameterSpec("SHA-256"), 20, 1))
+            .setProvider("BC").build(privKey);
+        X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+        
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+        cert.checkValidity(new Date());
+
+        //
+        // check verifies in general
+        //
+        cert.verify(pubKey);
+
+        //
+        // check verifies with contained key
+        //
+        cert.verify(cert.getPublicKey());
+
+        ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+        CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+        cert = (X509Certificate)fact.generateCertificate(bIn);
+
+        //System.out.println(cert);
+    }
+
+    /*
+     * we generate a self signed certificate across the range of ECDSA algorithms
+     */
+    public void checkCreationECDSA()
+        throws Exception
+    {
+        //
+        // set up the keys
+        //
+        KeyPairGenerator kpg = KeyPairGenerator.getInstance("EC", "BC");
+
+        kpg.initialize(256, new SecureRandom());
+
+        KeyPair kp = kpg.generateKeyPair();
+
+        PrivateKey privKey = kp.getPrivate();
+        PublicKey pubKey = kp.getPublic();
+
+        //
+        // distinguished name table.
+        //
+        X500NameBuilder builder = createStdBuilder();
+
+        String[] algs = new String[]
+            {
+                "SHA1WITHECDSA",
+                "ECDSAWITHSHA1",
+                "SHA224WITHECDSA",
+                "SHA256WITHECDSA",
+                "SHA384WITHECDSA",
+                "SHA512WITHECDSA",
+                "SHA3-224WITHECDSA",
+                "SHA3-256WITHECDSA",
+                "SHA3-384WITHECDSA",
+                "SHA3-512WITHECDSA"
+            };
+
+        ASN1ObjectIdentifier[] oids = new ASN1ObjectIdentifier[]
+            {
+                X9ObjectIdentifiers.ecdsa_with_SHA1,
+                X9ObjectIdentifiers.ecdsa_with_SHA1,
+                X9ObjectIdentifiers.ecdsa_with_SHA224,
+                X9ObjectIdentifiers.ecdsa_with_SHA256,
+                X9ObjectIdentifiers.ecdsa_with_SHA384,
+                X9ObjectIdentifiers.ecdsa_with_SHA512,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_224,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_256,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_384,
+                NISTObjectIdentifiers.id_ecdsa_with_sha3_512
+            };
+
+        doGenSelfSignedCert(privKey, pubKey, algs, oids);
+    }
+
+    private void doGenSelfSignedCert(PrivateKey privKey, PublicKey pubKey, String[] algs, ASN1ObjectIdentifier[] oids)
+        throws Exception
+    {
+        X500NameBuilder builder = createStdBuilder();
+
+        for (int i = 0; i != algs.length; i++)
+        {
+            //
+            // create the certificate - version 3
+            //
+            ContentSigner sigGen = new JcaContentSignerBuilder(algs[i]).setProvider("BC").build(privKey);
+            X509v3CertificateBuilder certGen = new JcaX509v3CertificateBuilder(builder.build(), BigInteger.valueOf(1), new Date(System.currentTimeMillis() - 50000), new Date(System.currentTimeMillis() + 50000), builder.build(), pubKey);
+
+            isEquals("oid mismatch", sigGen.getAlgorithmIdentifier().getAlgorithm(), oids[i]);
+
+            X509Certificate cert = new JcaX509CertificateConverter().setProvider(BC).getCertificate(certGen.build(sigGen));
+
+            cert.checkValidity(new Date());
+
+            //
+            // check verifies in general
+            //
+            cert.verify(pubKey);
+
+            //
+            // check verifies with contained key
+            //
+            cert.verify(cert.getPublicKey());
+
+            ByteArrayInputStream bIn = new ByteArrayInputStream(cert.getEncoded());
+            CertificateFactory fact = CertificateFactory.getInstance("X.509", BC);
+
+            cert = (X509Certificate)fact.generateCertificate(bIn);
+
+//            System.out.println(cert);
+        }
     }
 
     private void testForgedSignature()
@@ -3182,9 +3948,48 @@
         }
     }
 
+    private void checkSerialisation()
+        throws Exception
+    {
+        X509CertificateHolder crtHolder = new X509CertificateHolder(cert1);
+
+        doSerialize(crtHolder);
+
+        X509CRLHolder crlHolder = new X509CRLHolder(crl1);
+
+        doSerialize(crlHolder);
+
+        X509AttributeCertificateHolder attrHolder = new X509AttributeCertificateHolder(AttrCertTest.attrCert);
+
+        doSerialize(attrHolder);
+    }
+
+    private void doSerialize(Serializable encodable)
+        throws Exception
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        ObjectOutputStream oOut = new ObjectOutputStream(bOut);
+
+        oOut.writeObject(encodable);
+
+        oOut.close();
+
+        ObjectInputStream oIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+
+        Encodable obj = (Encodable)oIn.readObject();
+
+        isEquals(encodable, obj);
+        isEquals(encodable.hashCode(), obj.hashCode());
+    }
+
     public void performTest()
         throws Exception
     {
+        if (Security.getProvider("BCPQC") == null)
+        {
+            Security.addProvider(new BouncyCastlePQCProvider());
+        }
+        
         testDirect();
         testIndirect();
         testIndirect2();
@@ -3215,6 +4020,7 @@
         checkSelfSignedCertificate(18, gost34102001A);
         checkSelfSignedCertificate(19, sha3Cert);
 
+        checkSelfSignedCertificateAndKey(20, gost_2012_cert, "ECGOST3410-2012-256", gost_2012_privateKey);
         checkCRL(1, crl1);
 
         checkCreation1();
@@ -3224,6 +4030,14 @@
         checkCreation5();
 
         checkCreation6();
+        checkCreation7();
+        checkCreation8();
+        checkCreation9();
+
+        checkCreationQTESLA();
+        checkCreationDSA();
+        checkCreationECDSA();
+        checkCreationRSA();
 
         checkSm3WithSm2Creation();
 
@@ -3241,6 +4055,8 @@
         checkCRLCreation1();
         checkCRLCreation2();
         checkCRLCreation3();
+        checkCRLCreation4();
+        checkCRLCreation5();
 
         pemTest();
         pkcs7Test();
@@ -3253,6 +4069,8 @@
         checkCertificate(18, emptyDNCert);
 
         zeroDataTest();
+
+        checkSerialisation();
     }
 
     private Extensions generateExtensions(Vector oids, Vector values)
@@ -3283,7 +4101,7 @@
         String[] args)
     {
         Security.addProvider(new BouncyCastleProvider());
-
+        
         runTest(new CertTest());
     }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/cert/test/GOST3410_2012CMSTest.java b/bcpkix/src/main/java/org/bouncycastle/cert/test/GOST3410_2012CMSTest.java
new file mode 100644
index 0000000..6839bdf
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/GOST3410_2012CMSTest.java
@@ -0,0 +1,241 @@
+package org.bouncycastle.cert.test;
+
+import java.io.IOException;
+import java.math.BigInteger;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.util.Collection;
+import java.util.Date;
+import java.util.Iterator;
+
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.x500.X500Name;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x509.BasicConstraints;
+import org.bouncycastle.asn1.x509.ExtendedKeyUsage;
+import org.bouncycastle.asn1.x509.Extension;
+import org.bouncycastle.asn1.x509.KeyPurposeId;
+import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.X509v3CertificateBuilder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.CMSSignedDataGenerator;
+import org.bouncycastle.cms.CMSTypedData;
+import org.bouncycastle.cms.SignerId;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.SignerInformationVerifier;
+import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECDomainParameters;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.params.ECPublicKeyParameters;
+import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
+import org.bouncycastle.jce.interfaces.ECPrivateKey;
+import org.bouncycastle.jce.interfaces.ECPublicKey;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.jce.spec.ECNamedCurveGenParameterSpec;
+import org.bouncycastle.jce.spec.ECParameterSpec;
+import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcContentSignerBuilder;
+import org.bouncycastle.operator.bc.BcECContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
+import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.test.SimpleTest;
+import org.bouncycastle.util.test.Test;
+import org.bouncycastle.util.test.TestResult;
+
+
+public class GOST3410_2012CMSTest
+    extends SimpleTest
+{
+
+    public String getName()
+    {
+        return "GOST3410 2012 CMS TEST";
+    }
+
+    public void performTest()
+        throws Exception
+    {
+        if (Security.getProvider("BC").containsKey("KeyFactory.ECGOST3410-2012"))
+        {
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-512-paramSetA", "GOST3411-2012-512WITHECGOST3410-2012-512",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId());
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-512-paramSetB", "GOST3411-2012-512WITHECGOST3410-2012-512",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId());
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-512-paramSetC", "GOST3411-2012-512WITHECGOST3410-2012-512",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.getId());
+            cmsTest("GOST-3410-2012", "Tc26-Gost-3410-12-256-paramSetA", "GOST3411-2012-256WITHECGOST3410-2012-256",
+                RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.getId());
+        }
+    }
+
+    public void cmsTest(String keyAlgorithm, String paramName, String signAlgorithm, String digestId)
+    {
+        try
+        {
+            KeyPairGenerator keyPairGenerator =
+                KeyPairGenerator.getInstance(keyAlgorithm, "BC");
+            keyPairGenerator.initialize(new ECNamedCurveGenParameterSpec(paramName), new SecureRandom());
+            KeyPair keyPair = keyPairGenerator.generateKeyPair();
+
+            X509CertificateHolder signingCertificate = selfSignedCertificate(keyPair, signAlgorithm);
+
+            // CMS
+            byte[] dataContent = new byte[]{1, 2, 3, 4, 33, 22, 11, 33, 52, 21, 23};
+            CMSTypedData cmsTypedData = new CMSProcessableByteArray(dataContent);
+
+
+            final JcaSignerInfoGeneratorBuilder signerInfoGeneratorBuilder = new JcaSignerInfoGeneratorBuilder(
+                new JcaDigestCalculatorProviderBuilder().setProvider("BC").build());
+
+            final ContentSigner contentSigner = new
+                JcaContentSignerBuilder(signAlgorithm).setProvider("BC")
+                .build(keyPair.getPrivate());
+
+            final SignerInfoGenerator signerInfoGenerator = signerInfoGeneratorBuilder.build(contentSigner, signingCertificate);
+
+            CMSSignedDataGenerator cmsSignedDataGenerator = new CMSSignedDataGenerator();
+
+            cmsSignedDataGenerator.addCertificate(signingCertificate);
+            cmsSignedDataGenerator.addSignerInfoGenerator(signerInfoGenerator);
+
+            CMSSignedData cmsSignedData = cmsSignedDataGenerator.generate(cmsTypedData, false);
+            if (cmsSignedData == null)
+            {
+                fail("Cant create CMS");
+            }
+
+            boolean algIdContains = false;
+            for (Iterator it = cmsSignedData.getDigestAlgorithmIDs().iterator(); it.hasNext();)
+            {
+                AlgorithmIdentifier algorithmIdentifier = (AlgorithmIdentifier)it.next();
+                if (algorithmIdentifier.getAlgorithm().getId().equals(digestId))
+                {
+                    algIdContains = true;
+                    break;
+                }
+            }
+            if (!algIdContains)
+            {
+                fail("identifier not valid");
+            }
+            boolean result = verify(cmsSignedData, cmsTypedData);
+            if (!result)
+            {
+                fail("Verification fails ");
+            }
+
+        }
+        catch (Exception ex)
+        {
+            ex.printStackTrace();
+            fail("fail with exception:", ex);
+        }
+    }
+
+    private boolean verify(CMSSignedData signature, CMSTypedData typedData)
+        throws CertificateException, OperatorCreationException, IOException, CMSException
+    {
+        CMSSignedData signedDataToVerify = new CMSSignedData(typedData, signature.getEncoded());
+        Store certs = signedDataToVerify.getCertificates();
+        SignerInformationStore signers = signedDataToVerify.getSignerInfos();
+        Collection<SignerInformation> c = signers.getSigners();
+        for (Iterator it = c.iterator(); it.hasNext();)
+        {
+            SignerInformation signer = (SignerInformation)it.next();
+            SignerId signerId = signer.getSID();
+            Collection certCollection = certs.getMatches(signerId);
+
+            Iterator certIt = certCollection.iterator();
+            Object certificate = certIt.next();
+            SignerInformationVerifier verifier =
+                new JcaSimpleSignerInfoVerifierBuilder()
+                    .setProvider("BC").build((X509CertificateHolder)certificate);
+
+
+            boolean result = signer.verify(verifier);
+            if (result)
+            {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private X509CertificateHolder selfSignedCertificate(KeyPair keyPair, String signatureAlgName)
+        throws IOException, OperatorCreationException
+    {
+
+        X500Name name = new X500Name("CN=BB, C=aa");
+        ECPublicKey k = (ECPublicKey)keyPair.getPublic();
+        ECParameterSpec s = k.getParameters();
+        ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(
+            k.getQ(),
+            new ECDomainParameters(s.getCurve(), s.getG(), s.getN()));
+
+        ECPrivateKey kk = (ECPrivateKey)keyPair.getPrivate();
+        ECParameterSpec ss = kk.getParameters();
+
+        ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(
+            kk.getD(),
+            new ECDomainParameters(ss.getCurve(), ss.getG(), ss.getN()));
+
+        AsymmetricKeyParameter publicKey = ecPublicKeyParameters;
+        AsymmetricKeyParameter privateKey = ecPrivateKeyParameters;
+        X509v3CertificateBuilder myCertificateGenerator = new X509v3CertificateBuilder(
+            name,
+            BigInteger.ONE,
+            new Date(),
+            new Date(new Date().getTime() + 364 * 50 * 3600),
+            name,
+            SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(publicKey));
+
+        DefaultSignatureAlgorithmIdentifierFinder signatureAlgorithmIdentifierFinder = new DefaultSignatureAlgorithmIdentifierFinder();
+        DefaultDigestAlgorithmIdentifierFinder digestAlgorithmIdentifierFinder = new DefaultDigestAlgorithmIdentifierFinder();
+
+        AlgorithmIdentifier signAlgId = signatureAlgorithmIdentifierFinder.find(signatureAlgName);
+        AlgorithmIdentifier digestAlgId = digestAlgorithmIdentifierFinder.find(signAlgId);
+
+        BcContentSignerBuilder signerBuilder = new BcECContentSignerBuilder(signAlgId, digestAlgId);
+
+        int val = KeyUsage.cRLSign;
+        val = val | KeyUsage.dataEncipherment;
+        val = val | KeyUsage.decipherOnly;
+        val = val | KeyUsage.digitalSignature;
+        val = val | KeyUsage.encipherOnly;
+        val = val | KeyUsage.keyAgreement;
+        val = val | KeyUsage.keyEncipherment;
+        val = val | KeyUsage.nonRepudiation;
+        myCertificateGenerator.addExtension(Extension.keyUsage, true, new KeyUsage(val));
+
+        myCertificateGenerator.addExtension(Extension.basicConstraints, true, new BasicConstraints(false));
+
+        myCertificateGenerator.addExtension(Extension.extendedKeyUsage, true, new ExtendedKeyUsage(KeyPurposeId.id_kp_timeStamping));
+
+
+        X509CertificateHolder holder = myCertificateGenerator.build(signerBuilder.build(privateKey));
+
+        return holder;
+    }
+
+    public static void main(String[] args)
+    {
+        Security.addProvider(new BouncyCastleProvider());
+        Test test = new GOST3410_2012CMSTest();
+        TestResult result = test.perform();
+        System.out.println(result);
+    }
+}
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 cd3e8af..135716c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cert/test/PKCS10Test.java
@@ -26,6 +26,7 @@
 import org.bouncycastle.asn1.x509.Extension;
 import org.bouncycastle.asn1.x509.Extensions;
 import org.bouncycastle.asn1.x509.KeyUsage;
+import org.bouncycastle.asn1.x9.X9ECParameters;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.jcajce.JcaX509ExtensionUtils;
 import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
@@ -265,15 +266,9 @@
     private void createECRequest(String algorithm, ASN1ObjectIdentifier algOid)
         throws Exception
     {
-        ECCurve.Fp curve = new ECCurve.Fp(
-            new BigInteger("6864797660130609714981900799081393217269435300143305409394463459185543183397656052122559640661454554977296311391480858037121987999716643812574028291115057151"), // q (or p)
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", 16),   // a
-            new BigInteger("0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", 16));  // b
-
-        ECParameterSpec spec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("0200C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66")), // G
-            new BigInteger("01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", 16)); // n
+        X9ECParameters x9 = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName("secp521r1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec spec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         ECPrivateKeySpec privKeySpec = new ECPrivateKeySpec(
             new BigInteger("5769183828869504557786041598510887460263120754767955773309066354712783118202294874205844512909370791582896372147797293913785865682804434049019366394746072023"), // d
@@ -567,15 +562,9 @@
         // elliptic curve openSSL
         KeyPairGenerator g = KeyPairGenerator.getInstance("ECDSA", "BC");
 
-        ECCurve curve = new ECCurve.Fp(
-            new BigInteger("883423532389192164791648750360308885314476597252960362792450860609699839"), // q
-            new BigInteger("7fffffffffffffffffffffff7fffffffffff8000000000007ffffffffffc", 16), // a
-            new BigInteger("6b016c3bdcf18941d0d654921475ca71a9db2fb27d1d37796185c2942c0a", 16)); // b
-
-        ECParameterSpec ecSpec = new ECParameterSpec(
-            curve,
-            curve.decodePoint(Hex.decode("020ffa963cdca8816ccc33b8642bedf905c3d358573d3f27fbbd3b3cb9aaaf")), // G
-            new BigInteger("883423532389192164791648750360308884807550341691627752275345424702807307")); // n
+        X9ECParameters x9 = org.bouncycastle.asn1.x9.ECNamedCurveTable.getByName("prime239v1");
+        ECCurve curve = x9.getCurve();
+        ECParameterSpec ecSpec = new ECParameterSpec(curve, x9.getG(), x9.getN(), x9.getH());
 
         g.initialize(ecSpec, new SecureRandom());
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java b/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
index 23f70d6..63717f6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cmc/SimplePKIResponse.java
@@ -40,7 +40,7 @@
      * Create a SimplePKIResponse from the passed in bytes.
      *
      * @param responseEncoding BER/DER encoding of the certificate.
-     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     * @throws CMCException in the event of corrupted data, or an incorrect structure.
      */
     public SimplePKIResponse(byte[] responseEncoding)
         throws CMCException
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java
index 17731c1..bb251f6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSAlgorithm.java
@@ -1,12 +1,14 @@
 package org.bouncycastle.cms;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -29,9 +31,14 @@
     public static final ASN1ObjectIdentifier  AES192_GCM      = NISTObjectIdentifiers.id_aes192_GCM.intern();
     public static final ASN1ObjectIdentifier  AES256_GCM      = NISTObjectIdentifiers.id_aes256_GCM.intern();
 
+//	public static final ASN1ObjectIdentifier  AES128_CBC_CMAC      = BSIObjectIdentifiers.id_aes128_CBC_CMAC;
+//	public static final ASN1ObjectIdentifier  AES192_CBC_CMAC      = BSIObjectIdentifiers.id_aes192_CBC_CMAC;
+//	public static final ASN1ObjectIdentifier  AES256_CBC_CMAC      = BSIObjectIdentifiers.id_aes256_CBC_CMAC;
+
     public static final ASN1ObjectIdentifier  CAMELLIA128_CBC = NTTObjectIdentifiers.id_camellia128_cbc.intern();
     public static final ASN1ObjectIdentifier  CAMELLIA192_CBC = NTTObjectIdentifiers.id_camellia192_cbc.intern();
     public static final ASN1ObjectIdentifier  CAMELLIA256_CBC = NTTObjectIdentifiers.id_camellia256_cbc.intern();
+    public static final ASN1ObjectIdentifier  GOST28147_GCFB  = CryptoProObjectIdentifiers.gostR28147_gcfb.intern();
     public static final ASN1ObjectIdentifier  SEED_CBC        = KISAObjectIdentifiers.id_seedCBC.intern();
 
     public static final ASN1ObjectIdentifier  DES_EDE3_WRAP   = PKCSObjectIdentifiers.id_alg_CMS3DESwrap.intern();
@@ -43,6 +50,9 @@
     public static final ASN1ObjectIdentifier  CAMELLIA256_WRAP = NTTObjectIdentifiers.id_camellia256_wrap.intern();
     public static final ASN1ObjectIdentifier  SEED_WRAP       = KISAObjectIdentifiers.id_npki_app_cmsSeed_wrap.intern();
 
+    public static final ASN1ObjectIdentifier  GOST28147_WRAP  = CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap.intern();
+    public static final ASN1ObjectIdentifier  GOST28147_CRYPTOPRO_WRAP  = CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.intern();
+
     public static final ASN1ObjectIdentifier  ECDH_SHA1KDF    = X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme.intern();
     public static final ASN1ObjectIdentifier  ECCDH_SHA1KDF    = X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme.intern();
     public static final ASN1ObjectIdentifier  ECMQV_SHA1KDF   = X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme.intern();
@@ -63,6 +73,15 @@
     public static final ASN1ObjectIdentifier  ECCDH_SHA512KDF    = SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme.intern();
     public static final ASN1ObjectIdentifier  ECMQV_SHA512KDF   = SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme.intern();
 
+    public static final ASN1ObjectIdentifier  ECDHGOST3410_2001    = CryptoProObjectIdentifiers.gostR3410_2001.intern();
+    public static final ASN1ObjectIdentifier  ECDHGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.intern();
+    public static final ASN1ObjectIdentifier  ECDHGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512.intern();
+
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF  = BSIObjectIdentifiers.ecka_eg_X963kdf;
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF_SHA256  = BSIObjectIdentifiers.ecka_eg_X963kdf_SHA256;
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF_SHA384  = BSIObjectIdentifiers.ecka_eg_X963kdf_SHA384;
+	public static final ASN1ObjectIdentifier  ECKA_EG_X963KDF_SHA512  = BSIObjectIdentifiers.ecka_eg_X963kdf_SHA512;
+
     public static final ASN1ObjectIdentifier  SHA1 = OIWObjectIdentifiers.idSHA1.intern();
     public static final ASN1ObjectIdentifier  SHA224 = NISTObjectIdentifiers.id_sha224.intern();
     public static final ASN1ObjectIdentifier  SHA256 = NISTObjectIdentifiers.id_sha256.intern();
@@ -70,6 +89,8 @@
     public static final ASN1ObjectIdentifier  SHA512 = NISTObjectIdentifiers.id_sha512.intern();
     public static final ASN1ObjectIdentifier  MD5 = PKCSObjectIdentifiers.md5.intern();
     public static final ASN1ObjectIdentifier  GOST3411 = CryptoProObjectIdentifiers.gostR3411.intern();
+    public static final ASN1ObjectIdentifier  GOST3411_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256.intern();
+    public static final ASN1ObjectIdentifier  GOST3411_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512.intern();
     public static final ASN1ObjectIdentifier  RIPEMD128 = TeleTrusTObjectIdentifiers.ripemd128.intern();
     public static final ASN1ObjectIdentifier  RIPEMD160 = TeleTrusTObjectIdentifiers.ripemd160.intern();
     public static final ASN1ObjectIdentifier  RIPEMD256 = TeleTrusTObjectIdentifiers.ripemd256.intern();
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java
index fd6782d..6a45155 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSConfig.java
@@ -24,11 +24,12 @@
      *
      * @param oid object identifier to map.
      * @param algorithmName algorithm name to use.
+     * @deprecated no longer required.
      */
     public static void setSigningDigestAlgorithmMapping(String oid, String algorithmName)
     {
         ASN1ObjectIdentifier id = new ASN1ObjectIdentifier(oid);
 
-        CMSSignedHelper.INSTANCE.setSigningDigestAlgorithmMapping(id, algorithmName);
+        //CMSSignedHelper.INSTANCE.setSigningDigestAlgorithmMapping(id, algorithmName);
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
index 1c79a94..d24608e 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableByteArray.java
@@ -21,7 +21,7 @@
     public CMSProcessableByteArray(
         byte[]  bytes)
     {
-        this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), bytes);
+        this(CMSObjectIdentifiers.data, bytes);
     }
 
     public CMSProcessableByteArray(
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java
index b1e4527..10b5d55 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSProcessableFile.java
@@ -32,7 +32,7 @@
         File file,
         int  bufSize)
     {
-        this(new ASN1ObjectIdentifier(CMSObjectIdentifiers.data.getId()), file, bufSize);
+        this(CMSObjectIdentifiers.data, file, bufSize);
     }
 
     public CMSProcessableFile(
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
index 1e09b48..04dbf52 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedDataStreamGenerator.java
@@ -2,6 +2,7 @@
 
 import java.io.IOException;
 import java.io.OutputStream;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.List;
 
@@ -15,6 +16,7 @@
 import org.bouncycastle.asn1.DERSet;
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
 /**
  * General class for generating a pkcs7-signature message stream.
@@ -188,14 +190,16 @@
         sigGen.addObject(calculateVersion(eContentType));
         
         ASN1EncodableVector  digestAlgs = new ASN1EncodableVector();
-        
+
         //
         // add the precalculated SignerInfo digest algorithms.
         //
         for (Iterator it = _signers.iterator(); it.hasNext();)
         {
             SignerInformation signer = (SignerInformation)it.next();
-            digestAlgs.add(CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID()));
+            AlgorithmIdentifier digAlg = CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID());
+
+            digestAlgs.add(digAlg);
         }
         
         //
@@ -228,6 +232,40 @@
         return new CmsSignedDataOutputStream(sigStream, eContentType, sGen, sigGen, eiGen);
     }
 
+    /**
+     * Return a list of the current Digest AlgorithmIdentifiers applying to the next signature.
+     *
+     * @return a list of the Digest AlgorithmIdentifiers
+     */
+    public List<AlgorithmIdentifier> getDigestAlgorithms()
+    {
+        List  digestAlorithms = new ArrayList();
+
+        //
+        // add the precalculated SignerInfo digest algorithms.
+        //
+        for (Iterator it = _signers.iterator(); it.hasNext();)
+        {
+            SignerInformation signer = (SignerInformation)it.next();
+            AlgorithmIdentifier digAlg = CMSSignedHelper.INSTANCE.fixAlgID(signer.getDigestAlgorithmID());
+
+            digestAlorithms.add(digAlg);
+        }
+
+        //
+        // add the new digests
+        //
+
+        for (Iterator it = signerGens.iterator(); it.hasNext();)
+        {
+            SignerInfoGenerator signerGen = (SignerInfoGenerator)it.next();
+
+            digestAlorithms.add(signerGen.getDigestAlgorithm());
+        }
+
+       return digestAlorithms;
+    }
+
     // RFC3852, section 5.1:
     // IF ((certificates is present) AND
     //    (any certificates with a type of other are present)) OR
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
index 9fe6779..37a697f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedGenerator.java
@@ -17,6 +17,7 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -50,6 +51,8 @@
     public static final String  ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS.getId();
     public static final String  ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94.getId();
     public static final String  ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001.getId();
+    public static final String  ENCRYPTION_ECGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256.getId();
+    public static final String  ENCRYPTION_ECGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512.getId();
 
     private static final String  ENCRYPTION_ECDSA_WITH_SHA1 = X9ObjectIdentifiers.ecdsa_with_SHA1.getId();
     private static final String  ENCRYPTION_ECDSA_WITH_SHA224 = X9ObjectIdentifiers.ecdsa_with_SHA224.getId();
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
index 2f98e69..1933fd4 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSSignedHelper.java
@@ -19,6 +19,7 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.AttributeCertificate;
@@ -37,81 +38,74 @@
     static final CMSSignedHelper INSTANCE = new CMSSignedHelper();
 
     private static final Map     encryptionAlgs = new HashMap();
-    private static final Map     digestAlgs = new HashMap();
-    private static final Map     digestAliases = new HashMap();
 
-    private static void addEntries(ASN1ObjectIdentifier alias, String digest, String encryption)
+    private static void addEntries(ASN1ObjectIdentifier alias, String encryption)
     {
-        digestAlgs.put(alias.getId(), digest);
         encryptionAlgs.put(alias.getId(), encryption);
     }
 
     static
     {
-        addEntries(NISTObjectIdentifiers.dsa_with_sha224, "SHA224", "DSA");
-        addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
-        addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
-        addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
-        addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
-        addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
-        addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
-        addEntries(OIWObjectIdentifiers.md5WithRSA, "MD5", "RSA");
-        addEntries(OIWObjectIdentifiers.sha1WithRSA, "SHA1", "RSA");
-        addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption, "MD2", "RSA");
-        addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
-        addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption, "MD5", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption, "SHA1", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption, "SHA224", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384", "RSA");
-        addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512", "RSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1, "SHA1", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224, "SHA224", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384", "ECDSA");
-        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512", "ECDSA");
-        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1", "DSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512", "ECDSA");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1, "SHA1", "RSA");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "SHA256", "RSA");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1, "SHA1", "RSAandMGF1");
-        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "SHA256", "RSAandMGF1");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha224, "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha256, "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha384, "DSA");
+        addEntries(NISTObjectIdentifiers.dsa_with_sha512,  "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_224, "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_256, "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_384,  "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_512,  "DSA");
+        addEntries(OIWObjectIdentifiers.dsaWithSHA1,  "DSA");
+        addEntries(OIWObjectIdentifiers.md4WithRSA, "RSA");
+        addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "RSA");
+        addEntries(OIWObjectIdentifiers.md5WithRSA,  "RSA");
+        addEntries(OIWObjectIdentifiers.sha1WithRSA,  "RSA");
+        addEntries(PKCSObjectIdentifiers.md2WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.md4WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.md5WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha1WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha224WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha256WithRSAEncryption, "RSA");
+        addEntries(PKCSObjectIdentifiers.sha384WithRSAEncryption,  "RSA");
+        addEntries(PKCSObjectIdentifiers.sha512WithRSAEncryption,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384,  "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512,  "RSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA1,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA224,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA256,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA384,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.ecdsa_with_SHA512, "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_224,  "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_256,  "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_512,  "ECDSA");
+        addEntries(X9ObjectIdentifiers.id_dsa_with_sha1,  "DSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_1,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_224,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_256,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_384,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_ECDSA_SHA_512,  "ECDSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_1,  "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_v1_5_SHA_256, "RSA");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_1,  "RSAandMGF1");
+        addEntries(EACObjectIdentifiers.id_TA_RSA_PSS_SHA_256, "RSAandMGF1");
 
-        encryptionAlgs.put(X9ObjectIdentifiers.id_dsa.getId(), "DSA");
-        encryptionAlgs.put(PKCSObjectIdentifiers.rsaEncryption.getId(), "RSA");
-        encryptionAlgs.put(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
-        encryptionAlgs.put(X509ObjectIdentifiers.id_ea_rsa.getId(), "RSA");
-        encryptionAlgs.put(CMSSignedDataGenerator.ENCRYPTION_RSA_PSS, "RSAandMGF1");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_94.getId(), "GOST3410");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001.getId(), "ECGOST3410");
-        encryptionAlgs.put("1.3.6.1.4.1.5849.1.6.2", "ECGOST3410");
-        encryptionAlgs.put("1.3.6.1.4.1.5849.1.1.5", "GOST3410");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001.getId(), "ECGOST3410");
-        encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94.getId(), "GOST3410");
-
-        digestAlgs.put(PKCSObjectIdentifiers.md2.getId(), "MD2");
-        digestAlgs.put(PKCSObjectIdentifiers.md4.getId(), "MD4");
-        digestAlgs.put(PKCSObjectIdentifiers.md5.getId(), "MD5");
-        digestAlgs.put(OIWObjectIdentifiers.idSHA1.getId(), "SHA1");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha224.getId(), "SHA224");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha256.getId(), "SHA256");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha384.getId(), "SHA384");
-        digestAlgs.put(NISTObjectIdentifiers.id_sha512.getId(), "SHA512");
-        digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128.getId(), "RIPEMD128");
-        digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160.getId(), "RIPEMD160");
-        digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256.getId(), "RIPEMD256");
-        digestAlgs.put(CryptoProObjectIdentifiers.gostR3411.getId(),  "GOST3411");
-        digestAlgs.put("1.3.6.1.4.1.5849.1.2.1",  "GOST3411");
-
-        digestAliases.put("SHA1", new String[] { "SHA-1" });
-        digestAliases.put("SHA224", new String[] { "SHA-224" });
-        digestAliases.put("SHA256", new String[] { "SHA-256" });
-        digestAliases.put("SHA384", new String[] { "SHA-384" });
-        digestAliases.put("SHA512", new String[] { "SHA-512" });
+        addEntries(X9ObjectIdentifiers.id_dsa, "DSA");
+        addEntries(PKCSObjectIdentifiers.rsaEncryption, "RSA");
+        addEntries(TeleTrusTObjectIdentifiers.teleTrusTRSAsignatureAlgorithm, "RSA");
+        addEntries(X509ObjectIdentifiers.id_ea_rsa, "RSA");
+        addEntries(PKCSObjectIdentifiers.id_RSASSA_PSS, "RSAandMGF1");
+        addEntries(CryptoProObjectIdentifiers.gostR3410_94, "GOST3410");
+        addEntries(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+        addEntries(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.6.2"), "ECGOST3410");
+        addEntries(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.1.5"), "GOST3410");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, "ECGOST3410-2012-256");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, "ECGOST3410-2012-512");
+        addEntries(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
+        addEntries(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "ECGOST3410-2012-256");
+        addEntries(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "ECGOST3410-2012-512");
     }
 
 
@@ -145,12 +139,7 @@
 
     void setSigningEncryptionAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
     {
-        encryptionAlgs.put(oid.getId(), algorithmName);
-    }
-
-    void setSigningDigestAlgorithmMapping(ASN1ObjectIdentifier oid, String algorithmName)
-    {
-        digestAlgs.put(oid.getId(), algorithmName);
+        addEntries(oid, algorithmName);
     }
 
     Store getCertificates(ASN1Set certSet)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
index f63bf3c..9204b5b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/CMSUtils.java
@@ -24,11 +24,15 @@
 import org.bouncycastle.asn1.cms.CMSObjectIdentifiers;
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.OtherRevocationInfoFormat;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.ocsp.OCSPResponse;
 import org.bouncycastle.asn1.ocsp.OCSPResponseStatus;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
+import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CRLHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
@@ -42,6 +46,9 @@
 class CMSUtils
 {
     private static final Set<String> des = new HashSet<String>();
+    private static final Set mqvAlgs = new HashSet();
+    private static final Set ecAlgs = new HashSet();
+    private static final Set gostAlgs = new HashSet();
 
     static
     {
@@ -51,6 +58,47 @@
         des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
         des.add(PKCSObjectIdentifiers.des_EDE3_CBC.getId());
         des.add(PKCSObjectIdentifiers.id_alg_CMS3DESwrap.getId());
+
+        mqvAlgs.add(X9ObjectIdentifiers.mqvSinglePass_sha1kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha224kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha256kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha384kdf_scheme);
+        mqvAlgs.add(SECObjectIdentifiers.mqvSinglePass_sha512kdf_scheme);
+
+        ecAlgs.add(X9ObjectIdentifiers.dhSinglePass_cofactorDH_sha1kdf_scheme);
+        ecAlgs.add(X9ObjectIdentifiers.dhSinglePass_stdDH_sha1kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha224kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha224kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha256kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha256kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha384kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme);
+        ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme);
+
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+    }
+
+    static boolean isMQV(ASN1ObjectIdentifier algorithm)
+    {
+        return mqvAlgs.contains(algorithm);
+    }
+
+    static boolean isEC(ASN1ObjectIdentifier algorithm)
+    {
+        return ecAlgs.contains(algorithm);
+    }
+
+    static boolean isGOST(ASN1ObjectIdentifier algorithm)
+    {
+        return gostAlgs.contains(algorithm);
+    }
+
+    static boolean isRFC2631(ASN1ObjectIdentifier algorithm)
+    {
+        return algorithm.equals(PKCSObjectIdentifiers.id_alg_ESDH) || algorithm.equals(PKCSObjectIdentifiers.id_alg_SSDH);
     }
 
     static boolean isDES(String algorithmID)
@@ -96,7 +144,7 @@
     {
         // enforce some limit checking
         return readContentInfo(new ASN1InputStream(input));
-    } 
+    }
 
     static List getCertificatesFromStore(Store certStore)
         throws CMSException
@@ -105,7 +153,7 @@
 
         try
         {
-            for (Iterator it = certStore.getMatches(null).iterator(); it.hasNext();)
+            for (Iterator it = certStore.getMatches(null).iterator(); it.hasNext(); )
             {
                 X509CertificateHolder c = (X509CertificateHolder)it.next();
 
@@ -127,7 +175,7 @@
 
         try
         {
-            for (Iterator it = attrStore.getMatches(null).iterator(); it.hasNext();)
+            for (Iterator it = attrStore.getMatches(null).iterator(); it.hasNext(); )
             {
                 X509AttributeCertificateHolder attrCert = (X509AttributeCertificateHolder)it.next();
 
@@ -150,7 +198,7 @@
 
         try
         {
-            for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext();)
+            for (Iterator it = crlStore.getMatches(null).iterator(); it.hasNext(); )
             {
                 Object rev = it.next();
 
@@ -199,7 +247,7 @@
     {
         List others = new ArrayList();
 
-        for (Iterator it = otherRevocationInfos.getMatches(null).iterator(); it.hasNext();)
+        for (Iterator it = otherRevocationInfos.getMatches(null).iterator(); it.hasNext(); )
         {
             ASN1Encodable info = (ASN1Encodable)it.next();
             OtherRevocationInfoFormat infoFormat = new OtherRevocationInfoFormat(otherRevocationInfoFormat, info);
@@ -216,7 +264,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
 
-        for (Iterator it = derObjects.iterator(); it.hasNext();)
+        for (Iterator it = derObjects.iterator(); it.hasNext(); )
         {
             v.add((ASN1Encodable)it.next());
         }
@@ -228,7 +276,7 @@
     {
         ASN1EncodableVector v = new ASN1EncodableVector();
 
-        for (Iterator it = derObjects.iterator(); it.hasNext();)
+        for (Iterator it = derObjects.iterator(); it.hasNext(); )
         {
             v.add((ASN1Encodable)it.next());
         }
@@ -237,7 +285,8 @@
     }
 
     static OutputStream createBEROctetOutputStream(OutputStream s,
-            int tagNo, boolean isExplicit, int bufferSize) throws IOException
+                                                   int tagNo, boolean isExplicit, int bufferSize)
+        throws IOException
     {
         BEROctetStringGenerator octGen = new BEROctetStringGenerator(s, tagNo, isExplicit);
 
@@ -278,7 +327,7 @@
     }
 
     public static byte[] streamToByteArray(
-        InputStream in) 
+        InputStream in)
         throws IOException
     {
         return Streams.readAll(in);
@@ -286,7 +335,7 @@
 
     public static byte[] streamToByteArray(
         InputStream in,
-        int         limit)
+        int limit)
         throws IOException
     {
         return Streams.readAllLimited(in, limit);
@@ -322,10 +371,10 @@
     }
 
     static OutputStream getSafeTeeOutputStream(OutputStream s1,
-            OutputStream s2)
+                                               OutputStream s2)
     {
         return s1 == null ? getSafeOutputStream(s2)
-                : s2 == null ? getSafeOutputStream(s1) : new TeeOutputStream(
-                        s1, s2);
+            : s2 == null ? getSafeOutputStream(s1) : new TeeOutputStream(
+            s1, s2);
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
index 20e1cb5..db45ec4 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/DefaultCMSSignatureAlgorithmNameGenerator.java
@@ -7,9 +7,11 @@
 import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.X509ObjectIdentifiers;
@@ -33,6 +35,18 @@
         addEntries(NISTObjectIdentifiers.dsa_with_sha256, "SHA256", "DSA");
         addEntries(NISTObjectIdentifiers.dsa_with_sha384, "SHA384", "DSA");
         addEntries(NISTObjectIdentifiers.dsa_with_sha512, "SHA512", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_224, "SHA3-224", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_256, "SHA3-256", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_384, "SHA3-384", "DSA");
+        addEntries(NISTObjectIdentifiers.id_dsa_with_sha3_512, "SHA3-512", "DSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, "SHA3-224", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, "SHA3-256", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, "SHA3-384", "RSA");
+        addEntries(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, "SHA3-512", "RSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_224, "SHA3-224", "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_256, "SHA3-256", "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "SHA3-384", "ECDSA");
+        addEntries(NISTObjectIdentifiers.id_ecdsa_with_sha3_512, "SHA3-512", "ECDSA");
         addEntries(OIWObjectIdentifiers.dsaWithSHA1, "SHA1", "DSA");
         addEntries(OIWObjectIdentifiers.md4WithRSA, "MD4", "RSA");
         addEntries(OIWObjectIdentifiers.md4WithRSAEncryption, "MD4", "RSA");
@@ -82,8 +96,13 @@
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
         encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.6.2"), "ECGOST3410");
         encryptionAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.1.5"), "GOST3410");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256, "ECGOST3410-2012-256");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512, "ECGOST3410-2012-512");
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "ECGOST3410");
         encryptionAlgs.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3410");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "ECGOST3410-2012-256");
+        encryptionAlgs.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "ECGOST3410-2012-512");
+        encryptionAlgs.put(GMObjectIdentifiers.sm2sign_with_sm3, "SM2");
 
         digestAlgs.put(PKCSObjectIdentifiers.md2, "MD2");
         digestAlgs.put(PKCSObjectIdentifiers.md4, "MD4");
@@ -93,11 +112,18 @@
         digestAlgs.put(NISTObjectIdentifiers.id_sha256, "SHA256");
         digestAlgs.put(NISTObjectIdentifiers.id_sha384, "SHA384");
         digestAlgs.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
+        digestAlgs.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
         digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
         digestAlgs.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
         digestAlgs.put(CryptoProObjectIdentifiers.gostR3411,  "GOST3411");
         digestAlgs.put(new ASN1ObjectIdentifier("1.3.6.1.4.1.5849.1.2.1"),  "GOST3411");
+        digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256,  "GOST3411-2012-256");
+        digestAlgs.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512,  "GOST3411-2012-512");
+        digestAlgs.put(GMObjectIdentifiers.sm3,  "SM3");
     }
 
     /**
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java
index 23b9680..8ea8089 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/KeyAgreeRecipientInfoGenerator.java
@@ -8,6 +8,8 @@
 import org.bouncycastle.asn1.cms.OriginatorIdentifierOrKey;
 import org.bouncycastle.asn1.cms.OriginatorPublicKey;
 import org.bouncycastle.asn1.cms.RecipientInfo;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789KeyWrapParameters;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
@@ -31,13 +33,17 @@
         throws CMSException
     {
         OriginatorIdentifierOrKey originator = new OriginatorIdentifierOrKey(
-                createOriginatorPublicKey(originatorKeyInfo));
+            createOriginatorPublicKey(originatorKeyInfo));
 
         AlgorithmIdentifier keyEncAlg;
         if (CMSUtils.isDES(keyEncryptionOID.getId()) || keyEncryptionOID.equals(PKCSObjectIdentifiers.id_alg_CMSRC2wrap))
         {
             keyEncAlg = new AlgorithmIdentifier(keyEncryptionOID, DERNull.INSTANCE);
         }
+        else if (CMSUtils.isGOST(keyAgreementOID))
+        {
+            keyEncAlg = new AlgorithmIdentifier(keyEncryptionOID, new Gost2814789KeyWrapParameters(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet));
+        }
         else
         {
             keyEncAlg = new AlgorithmIdentifier(keyEncryptionOID);
@@ -51,8 +57,7 @@
         if (userKeyingMaterial != null)
         {
             return new RecipientInfo(new KeyAgreeRecipientInfo(originator, new DEROctetString(userKeyingMaterial),
-                    keyAgreeAlg, recipients));
-
+                keyAgreeAlg, recipients));
         }
         else
         {
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
index ac6dd0f..40edbfc 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/SignerInformation.java
@@ -111,6 +111,8 @@
         this.signature = info.getEncryptedDigest().getOctets();
         this.content = baseInfo.content;
         this.resultDigest = baseInfo.resultDigest;
+        this.signedAttributeValues = baseInfo.signedAttributeValues;
+        this.unsignedAttributeValues = baseInfo.unsignedAttributeValues;
     }
 
     public boolean isCounterSignature()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java
index a12c66b..6395ac0 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcCMSContentEncryptorBuilder.java
@@ -9,11 +9,9 @@
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
-import org.bouncycastle.crypto.StreamCipher;
-import org.bouncycastle.crypto.io.CipherOutputStream;
 import org.bouncycastle.crypto.params.KeyParameter;
+import org.bouncycastle.crypto.util.CipherFactory;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OutputEncryptor;
 import org.bouncycastle.util.Integers;
@@ -94,9 +92,9 @@
 
             encKey = new KeyParameter(keyGen.generateKey());
 
-            algorithmIdentifier = helper.generateAlgorithmIdentifier(encryptionOID, encKey, random);
+            algorithmIdentifier = helper.generateEncryptionAlgID(encryptionOID, encKey, random);
 
-            cipher = helper.createContentCipher(true, encKey, algorithmIdentifier);
+            cipher = EnvelopedDataHelper.createContentCipher(true, encKey, algorithmIdentifier);
         }
 
         public AlgorithmIdentifier getAlgorithmIdentifier()
@@ -106,14 +104,7 @@
 
         public OutputStream getOutputStream(OutputStream dOut)
         {
-            if (cipher instanceof BufferedBlockCipher)
-            {
-                return new CipherOutputStream(dOut, (BufferedBlockCipher)cipher);
-            }
-            else
-            {
-                return new CipherOutputStream(dOut, (StreamCipher)cipher);
-            }
+            return CipherFactory.createOutputStream(dOut, cipher);
         }
 
         public GenericKey getKey()
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java
index b571b9a..92cfd16 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/BcRSAKeyTransRecipientInfoGenerator.java
@@ -18,6 +18,6 @@
     public BcRSAKeyTransRecipientInfoGenerator(X509CertificateHolder recipientCert)
         throws IOException
     {
-        super(recipientCert, new BcRSAAsymmetricKeyWrapper(recipientCert.getSubjectPublicKeyInfo().getAlgorithmId(), recipientCert.getSubjectPublicKeyInfo()));
+        super(recipientCert, new BcRSAAsymmetricKeyWrapper(recipientCert.getSubjectPublicKeyInfo().getAlgorithm(), recipientCert.getSubjectPublicKeyInfo()));
     }
 }
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java b/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java
index d2c3996..3eebc95 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/bc/EnvelopedDataHelper.java
@@ -5,30 +5,16 @@
 import java.util.HashMap;
 import java.util.Map;
 
-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.kisa.KISAObjectIdentifiers;
-import org.bouncycastle.asn1.misc.CAST5CBCParameters;
-import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
-import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
-import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cms.CMSAlgorithm;
 import org.bouncycastle.cms.CMSException;
-import org.bouncycastle.crypto.BlockCipher;
-import org.bouncycastle.crypto.BufferedBlockCipher;
 import org.bouncycastle.crypto.CipherKeyGenerator;
 import org.bouncycastle.crypto.CipherParameters;
 import org.bouncycastle.crypto.ExtendedDigest;
-import org.bouncycastle.crypto.KeyGenerationParameters;
-import org.bouncycastle.crypto.StreamCipher;
 import org.bouncycastle.crypto.Wrapper;
 import org.bouncycastle.crypto.digests.SHA1Digest;
 import org.bouncycastle.crypto.digests.SHA224Digest;
@@ -36,20 +22,14 @@
 import org.bouncycastle.crypto.digests.SHA384Digest;
 import org.bouncycastle.crypto.digests.SHA512Digest;
 import org.bouncycastle.crypto.engines.AESEngine;
-import org.bouncycastle.crypto.engines.CAST5Engine;
 import org.bouncycastle.crypto.engines.DESEngine;
 import org.bouncycastle.crypto.engines.DESedeEngine;
 import org.bouncycastle.crypto.engines.RC2Engine;
-import org.bouncycastle.crypto.engines.RC4Engine;
 import org.bouncycastle.crypto.engines.RFC3211WrapEngine;
-import org.bouncycastle.crypto.generators.DESKeyGenerator;
-import org.bouncycastle.crypto.generators.DESedeKeyGenerator;
-import org.bouncycastle.crypto.modes.CBCBlockCipher;
-import org.bouncycastle.crypto.paddings.PKCS7Padding;
-import org.bouncycastle.crypto.paddings.PaddedBufferedBlockCipher;
 import org.bouncycastle.crypto.params.KeyParameter;
-import org.bouncycastle.crypto.params.ParametersWithIV;
-import org.bouncycastle.crypto.params.RC2Parameters;
+import org.bouncycastle.crypto.util.AlgorithmIdentifierFactory;
+import org.bouncycastle.crypto.util.CipherFactory;
+import org.bouncycastle.crypto.util.CipherKeyGeneratorFactory;
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.operator.bc.BcDigestProvider;
 
@@ -117,101 +97,16 @@
         MAC_ALG_NAMES.put(CMSAlgorithm.RC2_CBC, "RC2Mac");
     }
 
-    private static final short[] rc2Table = {
-        0xbd, 0x56, 0xea, 0xf2, 0xa2, 0xf1, 0xac, 0x2a, 0xb0, 0x93, 0xd1, 0x9c, 0x1b, 0x33, 0xfd, 0xd0,
-        0x30, 0x04, 0xb6, 0xdc, 0x7d, 0xdf, 0x32, 0x4b, 0xf7, 0xcb, 0x45, 0x9b, 0x31, 0xbb, 0x21, 0x5a,
-        0x41, 0x9f, 0xe1, 0xd9, 0x4a, 0x4d, 0x9e, 0xda, 0xa0, 0x68, 0x2c, 0xc3, 0x27, 0x5f, 0x80, 0x36,
-        0x3e, 0xee, 0xfb, 0x95, 0x1a, 0xfe, 0xce, 0xa8, 0x34, 0xa9, 0x13, 0xf0, 0xa6, 0x3f, 0xd8, 0x0c,
-        0x78, 0x24, 0xaf, 0x23, 0x52, 0xc1, 0x67, 0x17, 0xf5, 0x66, 0x90, 0xe7, 0xe8, 0x07, 0xb8, 0x60,
-        0x48, 0xe6, 0x1e, 0x53, 0xf3, 0x92, 0xa4, 0x72, 0x8c, 0x08, 0x15, 0x6e, 0x86, 0x00, 0x84, 0xfa,
-        0xf4, 0x7f, 0x8a, 0x42, 0x19, 0xf6, 0xdb, 0xcd, 0x14, 0x8d, 0x50, 0x12, 0xba, 0x3c, 0x06, 0x4e,
-        0xec, 0xb3, 0x35, 0x11, 0xa1, 0x88, 0x8e, 0x2b, 0x94, 0x99, 0xb7, 0x71, 0x74, 0xd3, 0xe4, 0xbf,
-        0x3a, 0xde, 0x96, 0x0e, 0xbc, 0x0a, 0xed, 0x77, 0xfc, 0x37, 0x6b, 0x03, 0x79, 0x89, 0x62, 0xc6,
-        0xd7, 0xc0, 0xd2, 0x7c, 0x6a, 0x8b, 0x22, 0xa3, 0x5b, 0x05, 0x5d, 0x02, 0x75, 0xd5, 0x61, 0xe3,
-        0x18, 0x8f, 0x55, 0x51, 0xad, 0x1f, 0x0b, 0x5e, 0x85, 0xe5, 0xc2, 0x57, 0x63, 0xca, 0x3d, 0x6c,
-        0xb4, 0xc5, 0xcc, 0x70, 0xb2, 0x91, 0x59, 0x0d, 0x47, 0x20, 0xc8, 0x4f, 0x58, 0xe0, 0x01, 0xe2,
-        0x16, 0x38, 0xc4, 0x6f, 0x3b, 0x0f, 0x65, 0x46, 0xbe, 0x7e, 0x2d, 0x7b, 0x82, 0xf9, 0x40, 0xb5,
-        0x1d, 0x73, 0xf8, 0xeb, 0x26, 0xc7, 0x87, 0x97, 0x25, 0x54, 0xb1, 0x28, 0xaa, 0x98, 0x9d, 0xa5,
-        0x64, 0x6d, 0x7a, 0xd4, 0x10, 0x81, 0x44, 0xef, 0x49, 0xd6, 0xae, 0x2e, 0xdd, 0x76, 0x5c, 0x2f,
-        0xa7, 0x1c, 0xc9, 0x09, 0x69, 0x9a, 0x83, 0xcf, 0x29, 0x39, 0xb9, 0xe9, 0x4c, 0xff, 0x43, 0xab
-    };
-
-    private static final short[] rc2Ekb = {
-        0x5d, 0xbe, 0x9b, 0x8b, 0x11, 0x99, 0x6e, 0x4d, 0x59, 0xf3, 0x85, 0xa6, 0x3f, 0xb7, 0x83, 0xc5,
-        0xe4, 0x73, 0x6b, 0x3a, 0x68, 0x5a, 0xc0, 0x47, 0xa0, 0x64, 0x34, 0x0c, 0xf1, 0xd0, 0x52, 0xa5,
-        0xb9, 0x1e, 0x96, 0x43, 0x41, 0xd8, 0xd4, 0x2c, 0xdb, 0xf8, 0x07, 0x77, 0x2a, 0xca, 0xeb, 0xef,
-        0x10, 0x1c, 0x16, 0x0d, 0x38, 0x72, 0x2f, 0x89, 0xc1, 0xf9, 0x80, 0xc4, 0x6d, 0xae, 0x30, 0x3d,
-        0xce, 0x20, 0x63, 0xfe, 0xe6, 0x1a, 0xc7, 0xb8, 0x50, 0xe8, 0x24, 0x17, 0xfc, 0x25, 0x6f, 0xbb,
-        0x6a, 0xa3, 0x44, 0x53, 0xd9, 0xa2, 0x01, 0xab, 0xbc, 0xb6, 0x1f, 0x98, 0xee, 0x9a, 0xa7, 0x2d,
-        0x4f, 0x9e, 0x8e, 0xac, 0xe0, 0xc6, 0x49, 0x46, 0x29, 0xf4, 0x94, 0x8a, 0xaf, 0xe1, 0x5b, 0xc3,
-        0xb3, 0x7b, 0x57, 0xd1, 0x7c, 0x9c, 0xed, 0x87, 0x40, 0x8c, 0xe2, 0xcb, 0x93, 0x14, 0xc9, 0x61,
-        0x2e, 0xe5, 0xcc, 0xf6, 0x5e, 0xa8, 0x5c, 0xd6, 0x75, 0x8d, 0x62, 0x95, 0x58, 0x69, 0x76, 0xa1,
-        0x4a, 0xb5, 0x55, 0x09, 0x78, 0x33, 0x82, 0xd7, 0xdd, 0x79, 0xf5, 0x1b, 0x0b, 0xde, 0x26, 0x21,
-        0x28, 0x74, 0x04, 0x97, 0x56, 0xdf, 0x3c, 0xf0, 0x37, 0x39, 0xdc, 0xff, 0x06, 0xa4, 0xea, 0x42,
-        0x08, 0xda, 0xb4, 0x71, 0xb0, 0xcf, 0x12, 0x7a, 0x4e, 0xfa, 0x6c, 0x1d, 0x84, 0x00, 0xc8, 0x7f,
-        0x91, 0x45, 0xaa, 0x2b, 0xc2, 0xb1, 0x8f, 0xd5, 0xba, 0xf2, 0xad, 0x19, 0xb2, 0x67, 0x36, 0xf7,
-        0x0f, 0x0a, 0x92, 0x7d, 0xe3, 0x9d, 0xe9, 0x90, 0x3e, 0x23, 0x27, 0x66, 0x13, 0xec, 0x81, 0x15,
-        0xbd, 0x22, 0xbf, 0x9f, 0x7e, 0xa9, 0x51, 0x4b, 0x4c, 0xfb, 0x02, 0xd3, 0x70, 0x86, 0x31, 0xe7,
-        0x3b, 0x05, 0x03, 0x54, 0x60, 0x48, 0x65, 0x18, 0xd2, 0xcd, 0x5f, 0x32, 0x88, 0x0e, 0x35, 0xfd
-    };
-
     EnvelopedDataHelper()
     {
     }
 
-    String getBaseCipherName(ASN1ObjectIdentifier algorithm)
-    {
-        String name = (String)BASE_CIPHER_NAMES.get(algorithm);
-
-        if (name == null)
-        {
-            return algorithm.getId();
-        }
-
-        return name;
-    }
-
     static ExtendedDigest getPRF(AlgorithmIdentifier algID)
         throws OperatorCreationException
     {
         return ((BcDigestProvider)prfs.get(algID.getAlgorithm())).get(null);
     }
 
-    static BufferedBlockCipher createCipher(ASN1ObjectIdentifier algorithm)
-        throws CMSException
-    {
-        BlockCipher cipher;
-
-        if (NISTObjectIdentifiers.id_aes128_CBC.equals(algorithm)
-            || NISTObjectIdentifiers.id_aes192_CBC.equals(algorithm)
-            || NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new AESEngine());
-        }
-        else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new DESedeEngine());
-        }
-        else if (OIWObjectIdentifiers.desCBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new DESEngine());
-        }
-        else if (PKCSObjectIdentifiers.RC2_CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new RC2Engine());
-        }
-        else if (MiscObjectIdentifiers.cast5CBC.equals(algorithm))
-        {
-            cipher = new CBCBlockCipher(new CAST5Engine());
-        }
-        else
-        {
-            throw new CMSException("cannot recognise cipher: " + algorithm);
-        }
-
-        return new PaddedBufferedBlockCipher(cipher, new PKCS7Padding());
-    }
-
     static Wrapper createRFC3211Wrapper(ASN1ObjectIdentifier algorithm)
         throws CMSException
     {
@@ -242,201 +137,39 @@
     static Object createContentCipher(boolean forEncryption, CipherParameters encKey, AlgorithmIdentifier encryptionAlgID)
         throws CMSException
     {
-        ASN1ObjectIdentifier encAlg = encryptionAlgID.getAlgorithm();
-
-        if (encAlg.equals(PKCSObjectIdentifiers.rc4))
+        try
         {
-            StreamCipher cipher = new RC4Engine();
-
-            cipher.init(forEncryption, encKey);
-
-            return cipher;
+            return CipherFactory.createContentCipher(forEncryption, encKey, encryptionAlgID);
         }
-        else
+        catch (IllegalArgumentException e)
         {
-            BufferedBlockCipher cipher = createCipher(encryptionAlgID.getAlgorithm());
-            ASN1Primitive sParams = encryptionAlgID.getParameters().toASN1Primitive();
-
-            if (sParams != null && !(sParams instanceof ASN1Null))
-            {
-                if (encAlg.equals(CMSAlgorithm.DES_EDE3_CBC)
-                    || encAlg.equals(CMSAlgorithm.IDEA_CBC)
-                    || encAlg.equals(CMSAlgorithm.AES128_CBC)
-                    || encAlg.equals(CMSAlgorithm.AES192_CBC)
-                    || encAlg.equals(CMSAlgorithm.AES256_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAMELLIA128_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAMELLIA192_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAMELLIA256_CBC)
-                    || encAlg.equals(CMSAlgorithm.SEED_CBC)
-                    || encAlg.equals(OIWObjectIdentifiers.desCBC))
-                {
-                    cipher.init(forEncryption, new ParametersWithIV(encKey,
-                        ASN1OctetString.getInstance(sParams).getOctets()));
-                }
-                else if (encAlg.equals(CMSAlgorithm.CAST5_CBC))
-                {
-                    CAST5CBCParameters cbcParams = CAST5CBCParameters.getInstance(sParams);
-
-                    cipher.init(forEncryption, new ParametersWithIV(encKey, cbcParams.getIV()));
-                }
-                else if (encAlg.equals(CMSAlgorithm.RC2_CBC))
-                {
-                    RC2CBCParameter cbcParams = RC2CBCParameter.getInstance(sParams);
-
-                    cipher.init(forEncryption, new ParametersWithIV(new RC2Parameters(((KeyParameter)encKey).getKey(), rc2Ekb[cbcParams.getRC2ParameterVersion().intValue()]), cbcParams.getIV()));
-                }
-                else
-                {
-                    throw new CMSException("cannot match parameters");
-                }
-            }
-            else
-            {
-                if (encAlg.equals(CMSAlgorithm.DES_EDE3_CBC)
-                    || encAlg.equals(CMSAlgorithm.IDEA_CBC)
-                    || encAlg.equals(CMSAlgorithm.CAST5_CBC))
-                {
-                    cipher.init(forEncryption, new ParametersWithIV(encKey, new byte[8]));
-                }
-                else
-                {
-                    cipher.init(forEncryption, encKey);
-                }
-            }
-
-            return cipher;
+            throw new CMSException(e.getMessage(), e);
         }
     }
 
-    AlgorithmIdentifier generateAlgorithmIdentifier(ASN1ObjectIdentifier encryptionOID, CipherParameters encKey, SecureRandom random)
+    AlgorithmIdentifier generateEncryptionAlgID(ASN1ObjectIdentifier encryptionOID, KeyParameter encKey, SecureRandom random)
         throws CMSException
     {
-        if (encryptionOID.equals(CMSAlgorithm.AES128_CBC)
-                || encryptionOID.equals(CMSAlgorithm.AES192_CBC)
-                || encryptionOID.equals(CMSAlgorithm.AES256_CBC)
-                || encryptionOID.equals(CMSAlgorithm.CAMELLIA128_CBC)
-                || encryptionOID.equals(CMSAlgorithm.CAMELLIA192_CBC)
-                || encryptionOID.equals(CMSAlgorithm.CAMELLIA256_CBC)
-                || encryptionOID.equals(CMSAlgorithm.SEED_CBC))
+        try
         {
-            byte[] iv = new byte[16];
-
-            random.nextBytes(iv);
-
-            return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
+            return AlgorithmIdentifierFactory.generateEncryptionAlgID(encryptionOID, encKey.getKey().length * 8, random);
         }
-        else if (encryptionOID.equals(CMSAlgorithm.DES_EDE3_CBC)
-                || encryptionOID.equals(CMSAlgorithm.IDEA_CBC)
-                || encryptionOID.equals(OIWObjectIdentifiers.desCBC))
+        catch (IllegalArgumentException e)
         {
-            byte[] iv = new byte[8];
-
-            random.nextBytes(iv);
-
-            return new AlgorithmIdentifier(encryptionOID, new DEROctetString(iv));
-        }
-        else if (encryptionOID.equals(CMSAlgorithm.CAST5_CBC))
-        {
-            byte[] iv = new byte[8];
-
-            random.nextBytes(iv);
-
-            CAST5CBCParameters cbcParams = new CAST5CBCParameters(iv, ((KeyParameter)encKey).getKey().length * 8);
-
-            return new AlgorithmIdentifier(encryptionOID, cbcParams);
-        }
-        else if (encryptionOID.equals(PKCSObjectIdentifiers.rc4))
-        {
-            return new AlgorithmIdentifier(encryptionOID, DERNull.INSTANCE);
-        }
-        else if (encryptionOID.equals(PKCSObjectIdentifiers.RC2_CBC))
-        {
-            byte[] iv = new byte[8];
-
-            random.nextBytes(iv);
-
-            RC2CBCParameter cbcParams = new RC2CBCParameter(rc2Table[128], iv);
-
-            return new AlgorithmIdentifier(encryptionOID, cbcParams);
-        }
-        else
-        {
-            throw new CMSException("unable to match algorithm");
+            throw new CMSException(e.getMessage(), e);
         }
     }
 
     CipherKeyGenerator createKeyGenerator(ASN1ObjectIdentifier algorithm, SecureRandom random)
         throws CMSException
     {
-        if (NISTObjectIdentifiers.id_aes128_CBC.equals(algorithm))
+        try
         {
-            return createCipherKeyGenerator(random, 128);
+            return CipherKeyGeneratorFactory.createKeyGenerator(algorithm, random);
         }
-        else if (NISTObjectIdentifiers.id_aes192_CBC.equals(algorithm))
+        catch (IllegalArgumentException e)
         {
-            return createCipherKeyGenerator(random, 192);
+            throw new CMSException(e.getMessage(), e);
         }
-        else if (NISTObjectIdentifiers.id_aes256_CBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 256);
-        }
-        else if (PKCSObjectIdentifiers.des_EDE3_CBC.equals(algorithm))
-        {
-            DESedeKeyGenerator keyGen = new DESedeKeyGenerator();
-
-            keyGen.init(new KeyGenerationParameters(random, 192));
-
-            return keyGen;
-        }
-        else if (NTTObjectIdentifiers.id_camellia128_cbc.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (NTTObjectIdentifiers.id_camellia192_cbc.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 192);
-        }
-        else if (NTTObjectIdentifiers.id_camellia256_cbc.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 256);
-        }
-        else if (KISAObjectIdentifiers.id_seedCBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (CMSAlgorithm.CAST5_CBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (OIWObjectIdentifiers.desCBC.equals(algorithm))
-        {
-            DESKeyGenerator keyGen = new DESKeyGenerator();
-
-            keyGen.init(new KeyGenerationParameters(random, 64));
-
-            return keyGen;
-        }
-        else if (PKCSObjectIdentifiers.rc4.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else if (PKCSObjectIdentifiers.RC2_CBC.equals(algorithm))
-        {
-            return createCipherKeyGenerator(random, 128);
-        }
-        else
-        {
-            throw new CMSException("cannot recognise cipher: " + algorithm);
-        }
-
-    }
-
-    private CipherKeyGenerator createCipherKeyGenerator(SecureRandom random, int keySize)
-    {
-        CipherKeyGenerator keyGen = new CipherKeyGenerator();
-
-        keyGen.init(new KeyGenerationParameters(random, keySize));
-
-        return keyGen;
     }
 }
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 20f9ebb..df62fd6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/CMSUtils.java
@@ -12,7 +12,9 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.cms.IssuerAndSerialNumber;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.sec.SECObjectIdentifiers;
 import org.bouncycastle.asn1.x509.Certificate;
 import org.bouncycastle.asn1.x509.Extension;
@@ -24,6 +26,7 @@
 {
     private static final Set mqvAlgs = new HashSet();
     private static final Set ecAlgs = new HashSet();
+    private static final Set gostAlgs = new HashSet();
 
     static
     {
@@ -43,6 +46,13 @@
         ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha384kdf_scheme);
         ecAlgs.add(SECObjectIdentifiers.dhSinglePass_cofactorDH_sha512kdf_scheme);
         ecAlgs.add(SECObjectIdentifiers.dhSinglePass_stdDH_sha512kdf_scheme);
+
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512);
     }
 
     static boolean isMQV(ASN1ObjectIdentifier algorithm)
@@ -55,6 +65,11 @@
         return ecAlgs.contains(algorithm);
     }
 
+    static boolean isGOST(ASN1ObjectIdentifier algorithm)
+    {
+        return gostAlgs.contains(algorithm);
+    }
+
     static boolean isRFC2631(ASN1ObjectIdentifier algorithm)
     {
         return algorithm.equals(PKCSObjectIdentifiers.id_alg_ESDH) || algorithm.equals(PKCSObjectIdentifiers.id_alg_SSDH);
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 8cef68d..b2ddf01 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/EnvelopedDataHelper.java
@@ -35,6 +35,7 @@
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
@@ -74,6 +75,7 @@
         BASE_CIPHER_NAMES.put(CMSAlgorithm.CAMELLIA256_CBC, "Camellia");
         BASE_CIPHER_NAMES.put(CMSAlgorithm.SEED_CBC, "SEED");
         BASE_CIPHER_NAMES.put(PKCSObjectIdentifiers.rc4, "RC4");
+        BASE_CIPHER_NAMES.put(CryptoProObjectIdentifiers.gostR28147_gcfb, "GOST28147");
 
         CIPHER_ALG_NAMES.put(CMSAlgorithm.DES_CBC,  "DES/CBC/PKCS5Padding");
         CIPHER_ALG_NAMES.put(CMSAlgorithm.RC2_CBC,  "RC2/CBC/PKCS5Padding");
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java
index 0de417a..7cb6254 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JcaSimpleSignerInfoGeneratorBuilder.java
@@ -6,6 +6,7 @@
 import java.security.cert.X509Certificate;
 
 import org.bouncycastle.asn1.cms.AttributeTable;
+import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
 import org.bouncycastle.cms.CMSAttributeTableGenerator;
 import org.bouncycastle.cms.DefaultSignedAttributeTableGenerator;
@@ -111,6 +112,14 @@
         return this;
     }
 
+    public SignerInfoGenerator build(String algorithmName, PrivateKey privateKey, X509CertificateHolder certificate)
+        throws OperatorCreationException
+    {
+        ContentSigner contentSigner = helper.createContentSigner(algorithmName, privateKey);
+
+        return configureAndBuild().build(contentSigner, certificate);
+    }
+
     public SignerInfoGenerator build(String algorithmName, PrivateKey privateKey, X509Certificate certificate)
         throws OperatorCreationException, CertificateEncodingException
     {
@@ -120,7 +129,7 @@
     }
 
     public SignerInfoGenerator build(String algorithmName, PrivateKey privateKey, byte[] keyIdentifier)
-        throws OperatorCreationException, CertificateEncodingException
+        throws OperatorCreationException
     {
         ContentSigner contentSigner = helper.createContentSigner(algorithmName, privateKey);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
index 01c5791..147c6a1 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKTSKeyTransRecipient.java
@@ -76,7 +76,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current Recipient.
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java
index 14217d2..f46f05d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipient.java
@@ -25,16 +25,21 @@
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.cms.ecc.ECCCMSSharedInfo;
 import org.bouncycastle.asn1.cms.ecc.MQVuserKeyingMaterial;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
+import org.bouncycastle.asn1.cryptopro.Gost2814789KeyWrapParameters;
 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.cms.CMSException;
 import org.bouncycastle.cms.KeyAgreeRecipient;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
 import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
 import org.bouncycastle.operator.SecretKeySizeProvider;
+import org.bouncycastle.util.Arrays;
 import org.bouncycastle.util.Pack;
 
 public abstract class JceKeyAgreeRecipient
@@ -172,6 +177,13 @@
                     userKeyingMaterialSpec = new UserKeyingMaterialSpec(userKeyingMaterial.getOctets());
                 }
             }
+            else if (CMSUtils.isGOST(keyEncAlg.getAlgorithm()))
+            {
+                if (userKeyingMaterial != null)
+                {
+                    userKeyingMaterialSpec = new UserKeyingMaterialSpec(userKeyingMaterial.getOctets());
+                }
+            }
             else
             {
                 throw new CMSException("Unknown key agreement algorithm: " + keyEncAlg.getAlgorithm());
@@ -210,6 +222,19 @@
                 SecretKey agreedWrapKey = calculateAgreedWrapKey(keyEncryptionAlgorithm, wrapAlg,
                     senderPublicKey, userKeyingMaterial, recipientKey, ecc_cms_Generator);
 
+                if (wrapAlg.getAlgorithm().equals(CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap)
+                    || wrapAlg.getAlgorithm().equals(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap))
+                {
+                    Gost2814789EncryptedKey encKey = Gost2814789EncryptedKey.getInstance(encryptedContentEncryptionKey);
+                    Gost2814789KeyWrapParameters wrapParams = Gost2814789KeyWrapParameters.getInstance(wrapAlg.getParameters());
+             
+                    Cipher keyCipher = helper.createCipher(wrapAlg.getAlgorithm());
+
+                    keyCipher.init(Cipher.UNWRAP_MODE, agreedWrapKey, new GOST28147WrapParameterSpec(wrapParams.getEncryptionParamSet(), userKeyingMaterial.getOctets()));
+
+                    return keyCipher.unwrap(Arrays.concatenate(encKey.getEncryptedKey(), encKey.getMacKey()), helper.getBaseCipherName(contentEncryptionAlgorithm.getAlgorithm()), Cipher.SECRET_KEY);
+                }
+
                 return unwrapSessionKey(wrapAlg.getAlgorithm(), agreedWrapKey, contentEncryptionAlgorithm.getAlgorithm(), encryptedContentEncryptionKey);
             }
             catch (InvalidKeyException e)
diff --git a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java
index ec9bd10..5e14f1b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyAgreeRecipientInfoGenerator.java
@@ -20,6 +20,7 @@
 import javax.crypto.SecretKey;
 
 import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Sequence;
@@ -30,11 +31,14 @@
 import org.bouncycastle.asn1.cms.RecipientEncryptedKey;
 import org.bouncycastle.asn1.cms.RecipientKeyIdentifier;
 import org.bouncycastle.asn1.cms.ecc.MQVuserKeyingMaterial;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.KeyAgreeRecipientInfoGenerator;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
 import org.bouncycastle.jcajce.spec.MQVParameterSpec;
 import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.operator.DefaultSecretKeySizeProvider;
@@ -149,6 +153,7 @@
             try
             {
                 AlgorithmParameterSpec agreementParamSpec;
+                ASN1ObjectIdentifier keyEncAlg = keyEncryptionAlgorithm.getAlgorithm();
 
                 if (CMSUtils.isMQV(keyAgreementOID))
                 {
@@ -156,7 +161,7 @@
                 }
                 else if (CMSUtils.isEC(keyAgreementOID))
                 {
-                    byte[] ukmKeyingMaterial = ecc_cms_Generator.generateKDFMaterial(keyEncryptionAlgorithm, keySizeProvider.getKeySize(keyEncryptionAlgorithm.getAlgorithm()), userKeyingMaterial);
+                    byte[] ukmKeyingMaterial = ecc_cms_Generator.generateKDFMaterial(keyEncryptionAlgorithm, keySizeProvider.getKeySize(keyEncAlg), userKeyingMaterial);
 
                     agreementParamSpec = new UserKeyingMaterialSpec(ukmKeyingMaterial);
                 }
@@ -175,6 +180,17 @@
                         agreementParamSpec = null;
                     }
                 }
+                else if (CMSUtils.isGOST(keyAgreementOID))
+                {
+                    if (userKeyingMaterial != null)
+                    {
+                        agreementParamSpec = new UserKeyingMaterialSpec(userKeyingMaterial);
+                    }
+                    else
+                    {
+                        throw new CMSException("User keying material must be set for static keys.");
+                    }
+                }
                 else
                 {
                     throw new CMSException("Unknown key agreement algorithm: " + keyAgreementOID);
@@ -185,22 +201,43 @@
                 keyAgreement.init(senderPrivateKey, agreementParamSpec, random);
                 keyAgreement.doPhase(recipientPublicKey, true);
 
-                SecretKey keyEncryptionKey = keyAgreement.generateSecret(keyEncryptionAlgorithm.getAlgorithm().getId());
+                SecretKey keyEncryptionKey = keyAgreement.generateSecret(keyEncAlg.getId());
 
                 // Wrap the content encryption key with the agreement key
-                Cipher keyEncryptionCipher = helper.createCipher(keyEncryptionAlgorithm.getAlgorithm());
+                Cipher keyEncryptionCipher = helper.createCipher(keyEncAlg);
+                ASN1OctetString encryptedKey;
 
-                keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, random);
+                if (keyEncAlg.equals(CryptoProObjectIdentifiers.id_Gost28147_89_None_KeyWrap)
+                    || keyEncAlg.equals(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap))
+                {
+                    keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, new GOST28147WrapParameterSpec(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, userKeyingMaterial));
 
-                byte[] encryptedKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));
+                    byte[] encKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));
 
-                ASN1OctetString encryptedKey = new DEROctetString(encryptedKeyBytes);
+                    Gost2814789EncryptedKey encKey = new Gost2814789EncryptedKey(
+                        Arrays.copyOfRange(encKeyBytes, 0, encKeyBytes.length - 4),
+                        Arrays.copyOfRange(encKeyBytes, encKeyBytes.length - 4, encKeyBytes.length));
+
+                    encryptedKey = new DEROctetString(encKey.getEncoded(ASN1Encoding.DER));
+                }
+                else
+                {
+                    keyEncryptionCipher.init(Cipher.WRAP_MODE, keyEncryptionKey, random);
+
+                    byte[] encryptedKeyBytes = keyEncryptionCipher.wrap(helper.getJceKey(contentEncryptionKey));
+
+                    encryptedKey = new DEROctetString(encryptedKeyBytes);
+                }
 
                 recipientEncryptedKeys.add(new RecipientEncryptedKey(karId, encryptedKey));
             }
             catch (GeneralSecurityException e)
             {
-                throw new CMSException("Cannot perform agreement step: " + e.getMessage(), e);
+                throw new CMSException("cannot perform agreement step: " + e.getMessage(), e);
+            }
+            catch (IOException e)
+            {
+                throw new CMSException("unable to encode wrapped key: " + e.getMessage(), 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 61f32bb..a152b95 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipient.java
@@ -1,18 +1,32 @@
 package org.bouncycastle.cms.jcajce;
 
 import java.security.Key;
+import java.security.KeyFactory;
 import java.security.PrivateKey;
 import java.security.Provider;
+import java.security.PublicKey;
+import java.security.spec.X509EncodedKeySpec;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 
+import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
+
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
+import org.bouncycastle.asn1.cryptopro.GostR3410KeyTransport;
+import org.bouncycastle.asn1.cryptopro.GostR3410TransportParameters;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.KeyTransRecipient;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.operator.OperatorException;
 import org.bouncycastle.operator.jcajce.JceAsymmetricKeyUnwrapper;
+import org.bouncycastle.util.Arrays;
 
 public abstract class JceKeyTransRecipient
     implements KeyTransRecipient
@@ -63,12 +77,12 @@
      * the standard lookup table won't work. Use this method to establish a specific mapping from an
      * algorithm identifier to a specific algorithm.
      * <p>
-     *     For example:
+     * For example:
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
-     * @param algorithm  OID of algorithm in recipient.
+     *
+     * @param algorithm     OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current Recipient.
      */
@@ -110,7 +124,7 @@
 
     /**
      * Set the provider to use for content processing.  If providerName is null a "no provider" search will be
-     *  used to satisfy getInstance calls.
+     * used to satisfy getInstance calls.
      *
      * @param providerName the name of the provider to use.
      * @return this recipient.
@@ -128,6 +142,7 @@
      * 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.
      */
@@ -141,32 +156,68 @@
     protected Key extractSecretKey(AlgorithmIdentifier keyEncryptionAlgorithm, AlgorithmIdentifier encryptedKeyAlgorithm, byte[] encryptedEncryptionKey)
         throws CMSException
     {
-        JceAsymmetricKeyUnwrapper unwrapper = helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey).setMustProduceEncodableUnwrappedKey(unwrappedKeyMustBeEncodable);
-
-        if (!extraMappings.isEmpty())
+        if (CMSUtils.isGOST(keyEncryptionAlgorithm.getAlgorithm()))
         {
-            for (Iterator it = extraMappings.keySet().iterator(); it.hasNext();)
+            try
             {
-                ASN1ObjectIdentifier algorithm = (ASN1ObjectIdentifier)it.next();
+                GostR3410KeyTransport transport = GostR3410KeyTransport.getInstance(encryptedEncryptionKey);
 
-                unwrapper.setAlgorithmMapping(algorithm, (String)extraMappings.get(algorithm));
+                GostR3410TransportParameters transParams = transport.getTransportParameters();
+
+                KeyFactory keyFactory = helper.createKeyFactory(keyEncryptionAlgorithm.getAlgorithm());
+
+                PublicKey pubKey = keyFactory.generatePublic(new X509EncodedKeySpec(transParams.getEphemeralPublicKey().getEncoded()));
+
+                KeyAgreement agreement = helper.createKeyAgreement(keyEncryptionAlgorithm.getAlgorithm());
+
+                agreement.init(recipientKey, new UserKeyingMaterialSpec(transParams.getUkm()));
+
+                agreement.doPhase(pubKey, true);
+
+                SecretKey key = agreement.generateSecret(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.getId());
+
+                Cipher keyCipher = helper.createCipher(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap);
+
+                keyCipher.init(Cipher.UNWRAP_MODE, key, new GOST28147WrapParameterSpec(transParams.getEncryptionParamSet(), transParams.getUkm()));
+
+                Gost2814789EncryptedKey encKey = transport.getSessionEncryptedKey();
+
+                return keyCipher.unwrap(Arrays.concatenate(encKey.getEncryptedKey(), encKey.getMacKey()), helper.getBaseCipherName(encryptedKeyAlgorithm.getAlgorithm()), Cipher.SECRET_KEY);
+            }
+            catch (Exception e)
+            {
+                throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
             }
         }
-
-        try
+        else
         {
-            Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+            JceAsymmetricKeyUnwrapper unwrapper = helper.createAsymmetricUnwrapper(keyEncryptionAlgorithm, recipientKey).setMustProduceEncodableUnwrappedKey(unwrappedKeyMustBeEncodable);
 
-            if (validateKeySize)
+            if (!extraMappings.isEmpty())
             {
-                helper.keySizeCheck(encryptedKeyAlgorithm, key);
+                for (Iterator it = extraMappings.keySet().iterator(); it.hasNext(); )
+                {
+                    ASN1ObjectIdentifier algorithm = (ASN1ObjectIdentifier)it.next();
+
+                    unwrapper.setAlgorithmMapping(algorithm, (String)extraMappings.get(algorithm));
+                }
             }
 
-            return key;
-        }
-        catch (OperatorException e)
-        {
-            throw new CMSException("exception unwrapping key: " + e.getMessage(), e);
+            try
+            {
+                Key key = helper.getJceKey(encryptedKeyAlgorithm.getAlgorithm(), unwrapper.generateUnwrappedKey(encryptedKeyAlgorithm, encryptedEncryptionKey));
+
+                if (validateKeySize)
+                {
+                    helper.keySizeCheck(encryptedKeyAlgorithm, key);
+                }
+
+                return key;
+            }
+            catch (OperatorException e)
+            {
+                throw new CMSException("exception unwrapping key: " + e.getMessage(), 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 60a2ff2..f624c8c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/jcajce/JceKeyTransRecipientInfoGenerator.java
@@ -73,7 +73,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current RecipientInfoGenerator.
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 b271457..eb76983 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewEnvelopedDataTest.java
@@ -1,7 +1,9 @@
 package org.bouncycastle.cms.test;
 
+import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.math.BigInteger;
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.Key;
@@ -10,8 +12,10 @@
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.PrivateKey;
+import java.security.SecureRandom;
 import java.security.Security;
 import java.security.cert.CertificateEncodingException;
+import java.security.cert.CertificateFactory;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.ECPrivateKey;
 import java.security.spec.MGF1ParameterSpec;
@@ -45,12 +49,14 @@
 import org.bouncycastle.asn1.cms.EncryptedContentInfo;
 import org.bouncycastle.asn1.cms.EnvelopedData;
 import org.bouncycastle.asn1.cms.GCMParameters;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.kisa.KISAObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RC2CBCParameter;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.x500.X500Name;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.Extension;
@@ -62,6 +68,7 @@
 import org.bouncycastle.cms.CMSEnvelopedDataGenerator;
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSProcessableByteArray;
+import org.bouncycastle.cms.CMSTypedData;
 import org.bouncycastle.cms.KeyAgreeRecipientInformation;
 import org.bouncycastle.cms.KeyTransRecipientInformation;
 import org.bouncycastle.cms.OriginatorInfoGenerator;
@@ -87,6 +94,7 @@
 import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
 import org.bouncycastle.cms.jcajce.JcePasswordEnvelopedRecipient;
 import org.bouncycastle.cms.jcajce.JcePasswordRecipientInfoGenerator;
+import org.bouncycastle.jce.ECGOST3410NamedCurveTable;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.PEMKeyPair;
 import org.bouncycastle.openssl.PEMParser;
@@ -243,6 +251,286 @@
             "no34BspoV/i4f0uLhJap84bTHcF/ZRSXCmQOCRGdSvQkXHeNPI5Lus6lOHuU" +
             "vUDbQC8=");
 
+    // from RFC 4490
+
+    private byte[] gost3410_RecipCert = Base64.decode(
+        "MIIB0DCCAX8CECv1xh7CEb0Xx9zUYma0LiEwCAYGKoUDAgIDMG0xHzAdBgNVBAMM" +
+            "Fkdvc3RSMzQxMC0yMDAxIGV4YW1wbGUxEjAQBgNVBAoMCUNyeXB0b1BybzELMAkG" +
+            "A1UEBhMCUlUxKTAnBgkqhkiG9w0BCQEWGkdvc3RSMzQxMC0yMDAxQGV4YW1wbGUu" +
+            "Y29tMB4XDTA1MDgxNjE0MTgyMFoXDTE1MDgxNjE0MTgyMFowbTEfMB0GA1UEAwwW" +
+            "R29zdFIzNDEwLTIwMDEgZXhhbXBsZTESMBAGA1UECgwJQ3J5cHRvUHJvMQswCQYD" +
+            "VQQGEwJSVTEpMCcGCSqGSIb3DQEJARYaR29zdFIzNDEwLTIwMDFAZXhhbXBsZS5j" +
+            "b20wYzAcBgYqhQMCAhMwEgYHKoUDAgIkAAYHKoUDAgIeAQNDAARAhJVodWACGkB1" +
+            "CM0TjDGJLP3lBQN6Q1z0bSsP508yfleP68wWuZWIA9CafIWuD+SN6qa7flbHy7Df" +
+            "D2a8yuoaYDAIBgYqhQMCAgMDQQA8L8kJRLcnqeyn1en7U23Sw6pkfEQu3u0xFkVP" +
+            "vFQ/3cHeF26NG+xxtZPz3TaTVXdoiYkXYiD02rEx1bUcM97i");
+
+    private byte[] gost3410_2001_KeyTrans = Base64.decode(
+        "MIIBpwYJKoZIhvcNAQcDoIIBmDCCAZQCAQAxggFTMIIBTwIBADCBgTBtMR8wHQYD" +
+            "VQQDDBZHb3N0UjM0MTAtMjAwMSBleGFtcGxlMRIwEAYDVQQKDAlDcnlwdG9Qcm8x" +
+            "CzAJBgNVBAYTAlJVMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAtMjAwMUBleGFt" +
+            "cGxlLmNvbQIQK/XGHsIRvRfH3NRiZrQuITAcBgYqhQMCAhMwEgYHKoUDAgIkAAYH" +
+            "KoUDAgIeAQSBpzCBpDAoBCBqL6ghBpVon5/kR6qey2EVK35BYLxdjfv1PSgbGJr5" +
+            "dQQENm2Yt6B4BgcqhQMCAh8BoGMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwIC" +
+            "HgEDQwAEQE0rLzOQ5tyj3VUqzd/g7/sx93N+Tv+/eImKK8PNMZQESw5gSJYf28dd" +
+            "Em/askCKd7W96vLsNMsjn5uL3Z4SwPYECJeV4ywrrSsMMDgGCSqGSIb3DQEHATAd" +
+            "BgYqhQMCAhUwEwQIvBCLHwv/NCkGByqFAwICHwGADKqOch3uT7Mu4w+hNw==");
+
+    private byte[] gost3410_2001_KeyAgree = Base64.decode(
+        "MIIBpAYJKoZIhvcNAQcDoIIBlTCCAZECAQIxggFQoYIBTAIBA6BloWMwHAYGKoUD" +
+            "AgITMBIGByqFAwICJAAGByqFAwICHgEDQwAEQLNVOfRngZcrpcTZhB8n+4HtCDLm" +
+            "mtTyAHi4/4Nk6tIdsHg8ff4DwfQG5DvMFrnF9vYZNxwXuKCqx9GhlLOlNiChCgQI" +
+            "L/D20YZLMoowHgYGKoUDAgJgMBQGByqFAwICDQAwCQYHKoUDAgIfATCBszCBsDCB" +
+            "gTBtMR8wHQYDVQQDDBZHb3N0UjM0MTAtMjAwMSBleGFtcGxlMRIwEAYDVQQKDAlD" +
+            "cnlwdG9Qcm8xCzAJBgNVBAYTAlJVMSkwJwYJKoZIhvcNAQkBFhpHb3N0UjM0MTAt" +
+            "MjAwMUBleGFtcGxlLmNvbQIQK/XGHsIRvRfH3NRiZrQuIQQqMCgEIBajHOfOTukN" +
+            "8ex0aQRoHsefOu24Ox8dSn75pdnLGdXoBAST/YZ+MDgGCSqGSIb3DQEHATAdBgYq" +
+            "hQMCAhUwEwQItzXhegc1oh0GByqFAwICHwGADDmxivS/qeJlJbZVyQ==");
+
+    public byte[] gost2001_Rand_Cert = Base64.decode(
+        "MIIELDCCA9ugAwIBAgIENqPHFzAIBgYqhQMCAgMwgckxCzAJBgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHR" +
+            "g9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQ" +
+            "vjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UEAwwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQ" +
+            "odC10YDQs9C10LXQstC40YcwHhcNMTcwNzE1MTQwMDAwWhcNMzcwNzE1MTQwMDAwWjCByTELMAkGA1UEBhMCUlUxIDAeBgNV" +
+            "BAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQ" +
+            "oNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g" +
+            "0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhzBjMBwGBiqFAwICEzASBgcqhQMCAiQABgcqhQMCAh4BA0MA" +
+            "BEC0WD4VzaInvp+WfjF+XIdZeWMrNSJVxUM6d/acwVMPwetEBtr1U82Cgf2U5eoz6eHxaLsAVG+qbiiMwV/4GKsao4IBpTCC" +
+            "AaEwDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggr" +
+            "BgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E" +
+            "FgQUqcUQmyYjxhQ9t5JX327oLxMjtkcwgfkGA1UdIwSB8TCB7oAUqcUQmyYjxhQ9t5JX327oLxMjtkehgc+kgcwwgckxCzAJ" +
+            "BgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC9" +
+            "0LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UE" +
+            "Awwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQodC10YDQs9C10LXQstC40YeCBDajxxcwCAYGKoUDAgIDA0EA" +
+            "2rrXsssEqxuRPtVRa+vlrgoXUa9WV+24uZ1LzsiMehSOv/pUo7kJZwoA5VCedJw0C8dce6Uc6lDJkNzpHN40hA=="
+    );
+
+    public byte[] gost2001_Rand_Key = Base64.decode(
+        "MEUCAQAwHAYGKoUDAgJiMBIGByqFAwICJAAGByqFAwICHgEEIgQgDWFcH/5KjwIwXrMdyO5CBnJdoOVtKp7WMb4EIljc+K4="
+    );
+
+    public byte[] gost2001_Rand_Msg = Base64.decode(
+        "MIIB+AYJKoZIhvcNAQcDoIIB6TCCAeUCAQAxggGkMIIBoAIBADCB0jCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf" +
+            "0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy" +
+            "0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrR" +
+            "gdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIENqPHFzAcBgYqhQMCAhMwEgYHKoUDAgIkAAYHKoUDAgIeAQSBpzCBpDAo" +
+            "BCCbkNQAmR9ny2u5W8MvFHs8iO91uA2iCy+2nccpwOQ0agQE9BJtXaB4BgcqhQMCAh8BoGMwHAYGKoUDAgITMBIGByqFAwIC" +
+            "JAAGByqFAwICHgEDQwAEQOeSFV7jo7EvygKSgHH79eel7sgWu0yW4swAK81Pw8jHMazuL6SpTUqUWNPW1jf4aFFHQAQmrxWV" +
+            "maCQn7gSJl8ECFgM3TO2P26NMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIC4ytWGecO5AGByqFAwICHwGADIzrpurLkuk0" +
+            "xGGidg=="
+    );
+
+    public byte[] gost2001_Rand_Sender_Cert = Base64.decode(
+        "MIIERTCCA/SgAwIBAgIEUu7tIDAIBgYqhQMCAgMwgdExCzAJBgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHR" +
+            "g9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEoMCYGA1UECwwf0JTQtdC50YHRgtCy0YPRjtGJ0LjQ" +
+            "tSDQu9C40YbQsDEtMCsGA1UEDAwk0KTQuNC70L7RgdC+0LIg0Lgg0L/Rg9Cx0LvQuNGG0LjRgdGCMSYwJAYDVQQDDB3QldCy" +
+            "0LPQtdC90ZbQuSDQntC90aPQs9C40L3RijAeFw0xNzA3MTYxNDAwMDBaFw0zNzA3MTYxNDAwMDBaMIHRMQswCQYDVQQGEwJS" +
+            "VTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxKDAm" +
+            "BgNVBAsMH9CU0LXQudGB0YLQstGD0Y7RidC40LUg0LvQuNGG0LAxLTArBgNVBAwMJNCk0LjQu9C+0YHQvtCyINC4INC/0YPQ" +
+            "sdC70LjRhtC40YHRgjEmMCQGA1UEAwwd0JXQstCz0LXQvdGW0Lkg0J7QvdGj0LPQuNC90YowYzAcBgYqhQMCAhMwEgYHKoUD" +
+            "AgIkAAYHKoUDAgIeAQNDAARAM++vMY04j9Bvcn71wM9atNkRo4lCixrOR82HncQbwnyBS6R0BqRmL+Q32TzEYpslzRkQnj/z" +
+            "yORa31QVSRghQaOCAa4wggGqMA4GA1UdDwEB/wQEAwIB/jBjBgNVHSUEXDBaBggrBgEFBQcDAQYIKwYBBQUHAwIGCCsGAQUF" +
+            "BwMDBggrBgEFBQcDBAYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEFBQcDBwYIKwYBBQUHAwgGCCsGAQUFBwMJMA8GA1UdEwEB" +
+            "/wQFMAMBAf8wHQYDVR0OBBYEFCLkv9o8dmaV1StuS8QFO64FJXXXMIIBAQYDVR0jBIH5MIH2gBQi5L/aPHZmldUrbkvEBTuu" +
+            "BSV116GB16SB1DCB0TELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQ" +
+            "odC+0LLRgNC10LzQtdC90L3QuNC6MSgwJgYDVQQLDB/QlNC10LnRgdGC0LLRg9GO0YnQuNC1INC70LjRhtCwMS0wKwYDVQQM" +
+            "DCTQpNC40LvQvtGB0L7QsiDQuCDQv9GD0LHQu9C40YbQuNGB0YIxJjAkBgNVBAMMHdCV0LLQs9C10L3RltC5INCe0L3Ro9Cz" +
+            "0LjQvdGKggRS7u0gMAgGBiqFAwICAwNBAIMLOOeDFPnrGkC/QG/pvLRZhEeiVkGVgy/h5WJancJDouHzedhI+mJqBFEYRoIy" +
+            "4KP5Q93Bf1NClXwIfnTOxWo="
+    );
+
+    public byte[] gost2001_Rand_Sender_Key = Base64.decode(
+        "MEUCAQAwHAYGKoUDAgJiMBIGByqFAwICJAAGByqFAwICHgEEIgQgGmpna37puqaRGBZjUAX5UfWaL67C9rvxCpOIexI0KUM="
+    );
+
+    public byte[] gost2001_Rand_Reci_Cert = Base64.decode(
+        "MIIELDCCA9ugAwIBAgIERMAcpzAIBgYqhQMCAgMwgckxCzAJBgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHR" +
+            "g9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQ" +
+            "vjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UEAwwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQ" +
+            "odC10YDQs9C10LXQstC40YcwHhcNMTcwNzE2MTQwMDAwWhcNMzcwNzE2MTQwMDAwWjCByTELMAkGA1UEBhMCUlUxIDAeBgNV" +
+            "BAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQ" +
+            "oNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g" +
+            "0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhzBjMBwGBiqFAwICEzASBgcqhQMCAiQABgcqhQMCAh4BA0MA" +
+            "BEA6Dzd7VQJA7712CfHiH4L0TVcaH+iLJ6vHkfdgAvS+8mGt/L2H9qQP7O41SgDKQqtfrr+tHDig7/ft5Bl1TFNoo4IBpTCC" +
+            "AaEwDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYBBQUHAwMGCCsGAQUFBwMEBggr" +
+            "BgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E" +
+            "FgQU8ZLn4r4PajaqWCwLYW6XauO3XsgwgfkGA1UdIwSB8TCB7oAU8ZLn4r4PajaqWCwLYW6XauO3Xsihgc+kgcwwgckxCzAJ" +
+            "BgNVBAYTAlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC9" +
+            "0LjQujEfMB0GA1UECwwW0KDRg9C60L7QstC+0LTRgdGC0LLQvjEZMBcGA1UEDAwQ0KDQtdC00LDQutGC0L7RgDE7MDkGA1UE" +
+            "Awwy0J/Rg9GI0LrQuNC9INCQ0LvQtdC60YHQsNC90LTRgCDQodC10YDQs9C10LXQstC40YeCBETAHKcwCAYGKoUDAgIDA0EA" +
+            "Ul4Y7XAhFUEoTUwdue+wbyxk86SpIFwC6NuVjTSIF3F9ACxfz2N6iwHaRv6GTVRIAEjj5G/rhdxRivvC8hU4QQ=="
+    );
+
+    public byte[] gost2001_Rand_Reci_Key = Base64.decode(
+        "MEUCAQAwHAYGKoUDAgJiMBIGByqFAwICJAAGByqFAwICHgEEIgQg5oDAn/BdWX4RSfHeqZyHAo/CNAy+2a0Jq3Z922cYeSQ="
+    );
+
+    public byte[] gost2001_Rand_Gen_Msg = Base64.decode(
+        "MIICAQYJKoZIhvcNAQcDoIIB8jCCAe4CAQAxggGtoYIBqQIBA6BloWMwHAYGKoUDAgITMBIGByqFAwICJAAGByqFAwICHgED" +
+            "QwAEQDPvrzGNOI/Qb3J+9cDPWrTZEaOJQosazkfNh53EG8J8gUukdAakZi/kN9k8xGKbJc0ZEJ4/88jkWt9UFUkYIUGhCgQI" +
+            "SQHkq1IzGZ8wKAYGKoUDAgJgMB4GByqFAwICDQEwEwYHKoUDAgIfAQQISQHkq1IzGZ8wggEFMIIBATCB0jCByTELMAkGA1UE" +
+            "BhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6" +
+            "MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQ" +
+            "n9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIERMAcpwQqMCgEIA4jC8qro8xNnn+R" +
+            "JTNYpV8dSdw82e/pnqnyo21o+qZkBAT9DaUDMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIziBZysW+ewMGByqFAwICHwGA" +
+            "DFKaSCs2xd4ef/khFQ=="
+    );
+
+    public byte[] gost2012_Sender_Cert = Base64.decode(
+        "MIIETDCCA/mgAwIBAgIEB/tRdzAKBggqhQMHAQEDAjCB0TELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MSgwJgYDVQQLDB/QlNC10LnRgdGC0LLRg9GO0YnQ" +
+            "uNC1INC70LjRhtCwMS0wKwYDVQQMDCTQpNC40LvQvtGB0L7QsiDQuCDQv9GD0LHQu9C40YbQuNGB0YIxJjAkBgNVBAMMHdCV" +
+            "0LLQs9C10L3RltC5INCe0L3Ro9Cz0LjQvdGKMB4XDTE3MDcxNTE0MDAwMFoXDTM3MDcxNTE0MDAwMFowgdExCzAJBgNVBAYT" +
+            "AlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEo" +
+            "MCYGA1UECwwf0JTQtdC50YHRgtCy0YPRjtGJ0LjQtSDQu9C40YbQsDEtMCsGA1UEDAwk0KTQuNC70L7RgdC+0LIg0Lgg0L/R" +
+            "g9Cx0LvQuNGG0LjRgdGCMSYwJAYDVQQDDB3QldCy0LPQtdC90ZbQuSDQntC90aPQs9C40L3RijBmMB8GCCqFAwcBAQEBMBMG" +
+            "ByqFAwICJAAGCCqFAwcBAQICA0MABEAl9XE868NRYm3CQXCPO+BJlVi7kxORfoyRaHyWyKBFf4TYV4eEUF/WjAf3fAqsndp6" +
+            "v1DNqa3KS1R1yqn1Ug4do4IBrjCCAaowDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYI" +
+            "KwYBBQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYD" +
+            "VR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUzhoR/a0hWGOpy6GPEm7LBCJ3dLYwggEBBgNVHSMEgfkwgfaAFM4aEf2tIVhjqcuh" +
+            "jxJuywQid3S2oYHXpIHUMIHRMQswCQYDVQQGEwJSVTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNV" +
+            "BAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxKDAmBgNVBAsMH9CU0LXQudGB0YLQstGD0Y7RidC40LUg0LvQuNGG0LAxLTAr" +
+            "BgNVBAwMJNCk0LjQu9C+0YHQvtCyINC4INC/0YPQsdC70LjRhtC40YHRgjEmMCQGA1UEAwwd0JXQstCz0LXQvdGW0Lkg0J7Q" +
+            "vdGj0LPQuNC90YqCBAf7UXcwCgYIKoUDBwEBAwIDQQDcFDvbdfUu1087tslF70OeZgLW5QHRtPLUaldE9x1Geu2veJos9fZ7" +
+            "nqISVcd1wrf6FfADt3Tw2pQuG8mVCNUi"
+    );
+
+    public byte[] gost2012_Sender_Key = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQgYARzlWBWAJLs64jQbYW4UEXqFN/ChtWCSHqRgivT" +
+            "8Ds="
+    );
+
+    public byte[] gost2012_Reci_Cert = Base64.decode(
+        "MIIEMzCCA+CgAwIBAgIEe7X7RjAKBggqhQMHAQEDAjCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQ" +
+            "stC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGA" +
+            "INCh0LXRgNCz0LXQtdCy0LjRhzAeFw0xNzA3MTUxNDAwMDBaFw0zNzA3MTUxNDAwMDBaMIHJMQswCQYDVQQGEwJSVTEgMB4G" +
+            "A1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxHzAdBgNVBAsM" +
+            "FtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNVBAMMMtCf0YPRiNC60LjQ" +
+            "vSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHMGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEB" +
+            "AgIDQwAEQGQ4aJ3On0XqEt62PUfquYCAx0690AzlyE9IO8r5zkNKldvK4THC1IgBHkRzKiewquMm0YuYh76NI01uNjThOjyj" +
+            "ggGlMIIBoTAOBgNVHQ8BAf8EBAMCAf4wYwYDVR0lBFwwWgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUH" +
+            "AwQGCCsGAQUFBwMFBggrBgEFBQcDBgYIKwYBBQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCTAPBgNVHRMBAf8EBTADAQH/MB0G" +
+            "A1UdDgQWBBROPw+FggywJjV9aLLSKz2Cr0BD9zCB+QYDVR0jBIHxMIHugBROPw+FggywJjV9aLLSKz2Cr0BD96GBz6SBzDCB" +
+            "yTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQ" +
+            "tdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTsw" +
+            "OQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRh4IEe7X7RjAKBggqhQMH" +
+            "AQEDAgNBAJR6UhzmUlRzlbiCU8IjhrR15c2uFtcHqHaUfiO8XJ2bnOiwxADZbnqlN3Foul6QrTXa5Vu1UbA2hFobJeuDniQ="
+    );
+
+    public byte[] gost2012_Reci_Key = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQgbtgmrFxhZLQm9H1Gx0+BAVTP6ZVLu20KcmKNzdIh" +
+            "rKc="
+    );
+
+    public byte[] gost2012_Reci_Msg = Base64.decode(
+        "MIICBgYJKoZIhvcNAQcDoIIB9zCCAfMCAQAxggGyoYIBrgIBA6BooWYwHwYIKoUDBwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEB" +
+            "AgIDQwAEQCX1cTzrw1FibcJBcI874EmVWLuTE5F+jJFofJbIoEV/hNhXh4RQX9aMB/d8Cqyd2nq/UM2prcpLVHXKqfVSDh2h" +
+            "CgQIDIhh5975RYMwKgYIKoUDBwEBBgEwHgYHKoUDAgINATATBgcqhQMCAh8BBAgMiGHn3vlFgzCCAQUwggEBMIHSMIHJMQsw" +
+            "CQYDVQQGEwJSVTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3Q" +
+            "vdC40LoxHzAdBgNVBAsMFtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNV" +
+            "BAMMMtCf0YPRiNC60LjQvSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHAgR7tftGBCowKAQgLMyx3zUe" +
+            "56F7eAKUAezilo3fxp6M/E+YkVVUDgFadfcEBHMmXJMwOAYJKoZIhvcNAQcBMB0GBiqFAwICFTATBAhJHfyezbxrUQYHKoUD" +
+            "AgIfAYAMLLM89stnSyrWGWSW"
+    );
+
+    public byte[] gost2012_512_Sender_Cert = Base64.decode(
+        "MIIE0jCCBD6gAwIBAgIEMBwU/jAKBggqhQMHAQEDAzCB0TELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MSgwJgYDVQQLDB/QlNC10LnRgdGC0LLRg9GO0YnQ" +
+            "uNC1INC70LjRhtCwMS0wKwYDVQQMDCTQpNC40LvQvtGB0L7QsiDQuCDQv9GD0LHQu9C40YbQuNGB0YIxJjAkBgNVBAMMHdCV" +
+            "0LLQs9C10L3RltC5INCe0L3Ro9Cz0LjQvdGKMB4XDTE3MDcxNTE0MDAwMFoXDTM3MDcxNTE0MDAwMFowgdExCzAJBgNVBAYT" +
+            "AlJVMSAwHgYDVQQIDBfQoS7Qn9C40YLQtdGA0LHRg9GA0LPRijEfMB0GA1UECgwW0KHQvtCy0YDQtdC80LXQvdC90LjQujEo" +
+            "MCYGA1UECwwf0JTQtdC50YHRgtCy0YPRjtGJ0LjQtSDQu9C40YbQsDEtMCsGA1UEDAwk0KTQuNC70L7RgdC+0LIg0Lgg0L/R" +
+            "g9Cx0LvQuNGG0LjRgdGCMSYwJAYDVQQDDB3QldCy0LPQtdC90ZbQuSDQntC90aPQs9C40L3RijCBqjAhBggqhQMHAQEBAjAV" +
+            "BgkqhQMHAQIBAgEGCCqFAwcBAQIDA4GEAASBgLnNMC1uA9NjhZMyIotCn+4H+iqcTv5paCYmRIuIvWZO7OvUv3u9aWK5Lb0w" +
+            "CH2Imbg/ffZV84xSwbNST83w4IFh8u1mAnf302+uuqt62pBU3VtPOPt3RYRwEABSDuTlBP2VocXa2iP53HM09fxhS/AJ14eR" +
+            "K2oJ4cNpASXDH1mSo4IBrjCCAaowDgYDVR0PAQH/BAQDAgH+MGMGA1UdJQRcMFoGCCsGAQUFBwMBBggrBgEFBQcDAgYIKwYB" +
+            "BQUHAwMGCCsGAQUFBwMEBggrBgEFBQcDBQYIKwYBBQUHAwYGCCsGAQUFBwMHBggrBgEFBQcDCAYIKwYBBQUHAwkwDwYDVR0T" +
+            "AQH/BAUwAwEB/zAdBgNVHQ4EFgQUEImfPZM/dIJULOrK4d/vMchap9kwggEBBgNVHSMEgfkwgfaAFBCJnz2TP3SCVCzqyuHf" +
+            "7zHIWqfZoYHXpIHUMIHRMQswCQYDVQQGEwJSVTEgMB4GA1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoM" +
+            "FtCh0L7QstGA0LXQvNC10L3QvdC40LoxKDAmBgNVBAsMH9CU0LXQudGB0YLQstGD0Y7RidC40LUg0LvQuNGG0LAxLTArBgNV" +
+            "BAwMJNCk0LjQu9C+0YHQvtCyINC4INC/0YPQsdC70LjRhtC40YHRgjEmMCQGA1UEAwwd0JXQstCz0LXQvdGW0Lkg0J7QvdGj" +
+            "0LPQuNC90YqCBDAcFP4wCgYIKoUDBwEBAwMDgYEAKZRx05mBwO7VIzj1FFJcHlfbHuLF+XZbFZaVfWc32R+KLxBJ0t1RuQ34" +
+            "KtjQhu8/oU2rR/pKcmyHRw3nxJy+DExdj7sWJ01uWH6vBa+nsXS8OzSIg+wb9hlrFy0wZSkQjyNMtSiNg+On1yzFeI2fxuAY" +
+            "OtIKHdqht+V+6M0g8BA="
+    );
+
+    public byte[] gost2012_512_Sender_Key = Base64.decode(
+        "MGoCAQAwIQYIKoUDBwEBBgIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRCBEDYpenYz4GDc/sIGl34Cv1T4xtWDlt7FB28ghXT" +
+            "n4MXm43IvLwW3YclZbRz7V9W5lR0XoftGJ9q3ICv/IN2F+Dr"
+    );
+
+    public byte[] gost2012_512_Reci_Cert = Base64.decode(
+        "MIIEuTCCBCWgAwIBAgIECpLweDAKBggqhQMHAQEDAzCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQ" +
+            "stC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGA" +
+            "INCh0LXRgNCz0LXQtdCy0LjRhzAeFw0xNzA3MTUxNDAwMDBaFw0zNzA3MTUxNDAwMDBaMIHJMQswCQYDVQQGEwJSVTEgMB4G" +
+            "A1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxHzAdBgNVBAsM" +
+            "FtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNVBAMMMtCf0YPRiNC60LjQ" +
+            "vSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHMIGqMCEGCCqFAwcBAQECMBUGCSqFAwcBAgECAQYIKoUD" +
+            "BwEBAgMDgYQABIGAnZAIQhH/2nmSIZWfn+K3ftHGWbx1vrh/IeA43Q/z7h9jVPcVV3Csju92lgL5cnXyBAV90CVGw0/bCu1N" +
+            "CYUpC0EVx5OmTd54fqicmFgZLqEnX6sbCXvpgCdvXhyYl+h7PTGHcuwGsMXZlIKVQLq6quVKh/UI/IfGK5CcPkX0PVCjggGl" +
+            "MIIBoTAOBgNVHQ8BAf8EBAMCAf4wYwYDVR0lBFwwWgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUHAwQG" +
+            "CCsGAQUFBwMFBggrBgEFBQcDBgYIKwYBBQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCTAPBgNVHRMBAf8EBTADAQH/MB0GA1Ud" +
+            "DgQWBBRvBhSgd/YSnT1ldXAE2V92ksV6WzCB+QYDVR0jBIHxMIHugBRvBhSgd/YSnT1ldXAE2V92ksV6W6GBz6SBzDCByTEL" +
+            "MAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC9" +
+            "0L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYD" +
+            "VQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRh4IECpLweDAKBggqhQMHAQED" +
+            "AwOBgQDilJAjXm+OK+mkfOk2ij3qKj00+gyFzJbxtk8wKEG7QmvlOPQvywke1pmCh8b1Z48OFOdmfKnTLE/D4AI/MQECUb1h" +
+            "ChUfgfrSw0LY205tqxp6aqDtc2iPI7XHQAKE+jD819zubjCBzVDOiyRXatiRsEtfXPTBvqQdisM4rSw+OQ=="
+
+    );
+
+    public byte[] gost2012_512_Reci_Key = Base64.decode(
+        "MGoCAQAwIQYIKoUDBwEBBgIwFQYJKoUDBwECAQIBBggqhQMHAQECAwRCBEDbd6/MUJS1QjpkwGUCg8OtxzuxiU2qm2VDBDDN" +
+            "ZQ8/GtO12OiysmJHAXS9fpO1TRuyySw0r5r4x2g0NCWtVdQf"
+    );
+
+    public byte[] gost2012_512_Reci_Msg = Base64.decode(
+        "MIICTAYJKoZIhvcNAQcDoIICPTCCAjkCAQAxggH4oYIB9AIBA6CBraGBqjAhBggqhQMHAQEBAjAVBgkqhQMHAQIBAgEGCCqF" +
+            "AwcBAQIDA4GEAASBgLnNMC1uA9NjhZMyIotCn+4H+iqcTv5paCYmRIuIvWZO7OvUv3u9aWK5Lb0wCH2Imbg/ffZV84xSwbNS" +
+            "T83w4IFh8u1mAnf302+uuqt62pBU3VtPOPt3RYRwEABSDuTlBP2VocXa2iP53HM09fxhS/AJ14eRK2oJ4cNpASXDH1mSoQoE" +
+            "CGGh2agBkurNMCoGCCqFAwcBAQYCMB4GByqFAwICDQEwEwYHKoUDAgIfAQQIYaHZqAGS6s0wggEFMIIBATCB0jCByTELMAkG" +
+            "A1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3Q" +
+            "uNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQD" +
+            "DDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIECpLweAQqMCgEIBEN53tKgcd9" +
+            "VW9uczUiwSM0pS/a7/vKIvTIqnIR0E5pBAQ+WRdXMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIbDvPAW4Wm0UGByqFAwIC" +
+            "HwGADFMeOJyH3t7YSNgxsA=="
+    );
+
+    public byte[] gost2012_KeyTrans_Reci_Cert = Base64.decode(
+        "MIIEMzCCA+CgAwIBAgIEBSqgszAKBggqhQMHAQEDAjCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQ" +
+            "sdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQ" +
+            "stC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGA" +
+            "INCh0LXRgNCz0LXQtdCy0LjRhzAeFw0xNzA3MTYxNDAwMDBaFw0zNzA3MTYxNDAwMDBaMIHJMQswCQYDVQQGEwJSVTEgMB4G" +
+            "A1UECAwX0KEu0J/QuNGC0LXRgNCx0YPRgNCz0YoxHzAdBgNVBAoMFtCh0L7QstGA0LXQvNC10L3QvdC40LoxHzAdBgNVBAsM" +
+            "FtCg0YPQutC+0LLQvtC00YHRgtCy0L4xGTAXBgNVBAwMENCg0LXQtNCw0LrRgtC+0YAxOzA5BgNVBAMMMtCf0YPRiNC60LjQ" +
+            "vSDQkNC70LXQutGB0LDQvdC00YAg0KHQtdGA0LPQtdC10LLQuNGHMGYwHwYIKoUDBwEBAQEwEwYHKoUDAgIkAAYIKoUDBwEB" +
+            "AgIDQwAEQEG5/wUY0LkiqETYAZY6o5mrjwWQNBYbSIKghYgKzLgSv1RCuTEFXRIJQcMG0V80auKVZNty9kcvn9P0IcJpGfGj" +
+            "ggGlMIIBoTAOBgNVHQ8BAf8EBAMCAf4wYwYDVR0lBFwwWgYIKwYBBQUHAwEGCCsGAQUFBwMCBggrBgEFBQcDAwYIKwYBBQUH" +
+            "AwQGCCsGAQUFBwMFBggrBgEFBQcDBgYIKwYBBQUHAwcGCCsGAQUFBwMIBggrBgEFBQcDCTAPBgNVHRMBAf8EBTADAQH/MB0G" +
+            "A1UdDgQWBBQJwiUIQOJNbB0Fzh6ucd3uRE9QzDCB+QYDVR0jBIHxMIHugBQJwiUIQOJNbB0Fzh6ucd3uRE9QzKGBz6SBzDCB" +
+            "yTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQ" +
+            "tdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTsw" +
+            "OQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrRgdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRh4IEBSqgszAKBggqhQMH" +
+            "AQEDAgNBAKLmdCiVR9MWeoC+MNudXGny3l2uDBBttvhTli0gDEaQLnBFyvD+cfSLgsheoz8vwhyqD/6W3ATBMRiGjqNJjQE=");
+
+    public byte[] gost2012_KeyTrans_Reci_Key = Base64.decode(
+        "MEgCAQAwHwYIKoUDBwEBBgEwEwYHKoUDAgIkAAYIKoUDBwEBAgIEIgQgy+dPu0sLqJ/Fokomiu69lRA48HaPNkP7kmzDHOxP" +
+            "QFc="
+    );
+
+    public byte[] gost2012_KeyTrans_Msg = Base64.decode(
+        "MIIB/gYJKoZIhvcNAQcDoIIB7zCCAesCAQAxggGqMIIBpgIBADCB0jCByTELMAkGA1UEBhMCUlUxIDAeBgNVBAgMF9ChLtCf" +
+            "0LjRgtC10YDQsdGD0YDQs9GKMR8wHQYDVQQKDBbQodC+0LLRgNC10LzQtdC90L3QuNC6MR8wHQYDVQQLDBbQoNGD0LrQvtCy" +
+            "0L7QtNGB0YLQstC+MRkwFwYDVQQMDBDQoNC10LTQsNC60YLQvtGAMTswOQYDVQQDDDLQn9GD0YjQutC40L0g0JDQu9C10LrR" +
+            "gdCw0L3QtNGAINCh0LXRgNCz0LXQtdCy0LjRhwIEBSqgszAfBggqhQMHAQEBATATBgcqhQMCAiQABggqhQMHAQECAgSBqjCB" +
+            "pzAoBCBnHA+9wEUh7KIkYlboGbtxRfrTL1oPGU3Tzaw8/khaWgQE+N56jaB7BgcqhQMCAh8BoGYwHwYIKoUDBwEBAQEwEwYH" +
+            "KoUDAgIkAAYIKoUDBwEBAgIDQwAEQMbb4wVWm1EWIIXKDseCNE6JHmS+4fNh2uB+10Isg7g8/1Wvdh66IFir6fyp8NRwwMkU" +
+            "QM0dmAfcpN6M2RSj83wECMCTi+FRlTafMDgGCSqGSIb3DQEHATAdBgYqhQMCAhUwEwQIzZlyAleTrCEGByqFAwICHwGADIO7" +
+            "l43OVnBpGM+FjQ=="
+    );
+
     public NewEnvelopedDataTest()
     {
     }
@@ -1320,7 +1608,8 @@
         CMSEnvelopedData ed;
         RecipientInformationStore recipients;
         Collection c;
-        Iterator it;ContentInfo eContentInfo = ContentInfo.getInstance(edData);
+        Iterator it;
+        ContentInfo eContentInfo = ContentInfo.getInstance(edData);
 
         EnvelopedData envD = EnvelopedData.getInstance(eContentInfo.getContent());
 
@@ -1821,6 +2110,538 @@
         processInput(ecKey, expected, "ecdh/encSessH.asc", new AlgorithmIdentifier(CMSAlgorithm.AES256_WRAP));
     }
 
+    public void testGost3410_2012_KeyTransGen()
+        throws Exception
+    {
+        byte[] data = Strings.toByteArray("hello world!");
+
+
+        CMSEnvelopedDataGenerator cmsEnvelopedDataGenerator = new CMSEnvelopedDataGenerator();
+
+
+        X509Certificate cert = (X509Certificate)CertificateFactory
+                                                    .getInstance("X.509", "BC")
+                                                    .generateCertificate(new ByteArrayInputStream(gost2012_512_Reci_Cert));
+        JceKeyTransRecipientInfoGenerator jceKey = new JceKeyTransRecipientInfoGenerator(cert).setProvider("BC");
+        cmsEnvelopedDataGenerator.addRecipientInfoGenerator(jceKey);
+        CMSTypedData msg = new CMSProcessableByteArray(data);
+        OutputEncryptor encryptor = new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider("BC").build();
+        CMSEnvelopedData cmsEnvelopedData = cmsEnvelopedDataGenerator.generate(msg, encryptor);
+
+        byte[] encryptedData = cmsEnvelopedData.getEncoded();
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(encryptedData);
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", "BC");
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Reci_Key));
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        Iterator it = c.iterator();
+
+         while (it.hasNext())
+         {
+             RecipientInformation recipient = (RecipientInformation)it.next();
+
+             assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512.getId());
+
+             byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+             assertTrue(Arrays.equals(data, recData));
+         }
+
+    }
+
+    public void testGost3410_2001_KeyTrans()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new org.bouncycastle.jce.spec.ECPrivateKeySpec(
+            new BigInteger("0B293BE050D0082BDAE785631A6BAB68F35B42786D6DDA56AFAF169891040F77", 16),
+            ECGOST3410NamedCurveTable.getParameterSpec("GostR3410-2001-CryptoPro-XchA")));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost3410_2001_KeyTrans);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("sample text\n", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyTransRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost3410_RecipCert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_KeyTrans()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_KeyTrans_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2012_KeyTrans_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyTransRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_KeyTrans_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyAgree()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new org.bouncycastle.jce.spec.ECPrivateKeySpec(
+            new BigInteger("0B293BE050D0082BDAE785631A6BAB68F35B42786D6DDA56AFAF169891040F77", 16),
+            ECGOST3410NamedCurveTable.getParameterSpec("GostR3410-2001-CryptoPro-XchA")));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost3410_2001_KeyAgree);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("sample text\n", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost3410_RecipCert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyTransRand()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2001_Rand_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyTransEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello world!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyTransRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyAgreeRand()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2001_Rand_Gen_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_KeyAgree()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2012_Reci_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_512_KeyAgree()
+        throws Exception
+    {
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey privKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Reci_Key));
+
+        CMSEnvelopedData ed = new CMSEnvelopedData(gost2012_512_Reci_Msg);
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(privKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        RecipientId id = new JceKeyAgreeRecipientId((X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_512_Reci_Cert)));
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2001_KeyAgree_Creation()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        X509Certificate senderCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Sender_Cert));
+        X509Certificate reciCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2001_Rand_Reci_Cert));
+
+        byte[] data = Strings.toByteArray("Hello World! Hello World!");
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410", BC);
+
+        PrivateKey senderKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Sender_Key));
+        PrivateKey reciKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2001_Rand_Reci_Key));
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECDHGOST3410_2012_256,
+            senderKey, senderCert.getPublicKey(), CMSAlgorithm.GOST28147_CRYPTOPRO_WRAP).setProvider(BC);
+
+        byte[] ukm = new byte[8];
+        random.nextBytes(ukm);
+
+        recipientGenerator.addRecipient(reciCert);
+        recipientGenerator.setUserKeyingMaterial(ukm);
+
+        edGen.addRecipientInfoGenerator(recipientGenerator);
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider(BC).build());
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciKey).setProvider(BC));
+
+            assertEquals("Hello World! Hello World!", Strings.fromByteArray(recData));
+        }
+
+        RecipientId id = new JceKeyAgreeRecipientId(reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_256_KeyAgree_Creation()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        X509Certificate senderCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_Sender_Cert));
+        X509Certificate reciCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_Reci_Cert));
+
+        byte[] data = Strings.toByteArray("Hello World!");
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey senderKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_Sender_Key));
+        PrivateKey reciKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_Reci_Key));
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECDHGOST3410_2012_256,
+            senderKey, senderCert.getPublicKey(), CMSAlgorithm.GOST28147_CRYPTOPRO_WRAP).setProvider(BC);
+
+        byte[] ukm = new byte[8];
+        random.nextBytes(ukm);
+
+        recipientGenerator.addRecipient(reciCert);
+        recipientGenerator.setUserKeyingMaterial(ukm);
+
+        edGen.addRecipientInfoGenerator(recipientGenerator);
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider(BC).build());
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        RecipientId id = new JceKeyAgreeRecipientId(reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
+    public void testGost3410_2012_512_KeyAgree_Creation()
+        throws Exception
+    {
+        SecureRandom random = new SecureRandom();
+        CertificateFactory certFact = CertificateFactory.getInstance("X.509", BC);
+
+        X509Certificate senderCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_512_Sender_Cert));
+        X509Certificate reciCert = (X509Certificate)certFact.generateCertificate(new ByteArrayInputStream(gost2012_512_Reci_Cert));
+
+        byte[] data = Strings.toByteArray("Hello World!");
+        KeyFactory keyFact = KeyFactory.getInstance("ECGOST3410-2012", BC);
+
+        PrivateKey senderKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Sender_Key));
+        PrivateKey reciKey = keyFact.generatePrivate(new PKCS8EncodedKeySpec(gost2012_512_Reci_Key));
+
+        CMSEnvelopedDataGenerator edGen = new CMSEnvelopedDataGenerator();
+
+        JceKeyAgreeRecipientInfoGenerator recipientGenerator = new JceKeyAgreeRecipientInfoGenerator(CMSAlgorithm.ECDHGOST3410_2012_512,
+            senderKey, senderCert.getPublicKey(), CMSAlgorithm.GOST28147_CRYPTOPRO_WRAP).setProvider(BC);
+
+        byte[] ukm = new byte[8];
+        random.nextBytes(ukm);
+
+        recipientGenerator.addRecipient(reciCert);
+        recipientGenerator.setUserKeyingMaterial(ukm);
+
+        edGen.addRecipientInfoGenerator(recipientGenerator);
+
+        CMSEnvelopedData ed = edGen.generate(
+            new CMSProcessableByteArray(data),
+            new JceCMSContentEncryptorBuilder(CMSAlgorithm.GOST28147_GCFB).setProvider(BC).build());
+
+        RecipientInformationStore recipients = ed.getRecipientInfos();
+
+        assertEquals(ed.getEncryptionAlgOID(), CryptoProObjectIdentifiers.gostR28147_gcfb.getId());
+
+        Collection c = recipients.getRecipients();
+
+        assertEquals(1, c.size());
+
+        Iterator it = c.iterator();
+
+        while (it.hasNext())
+        {
+            RecipientInformation recipient = (RecipientInformation)it.next();
+
+            assertEquals(recipient.getKeyEncryptionAlgOID(), RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512.getId());
+
+            byte[] recData = recipient.getContent(new JceKeyAgreeEnvelopedRecipient(reciKey).setProvider(BC));
+
+            assertEquals("Hello World!", Strings.fromByteArray(recData));
+        }
+
+        RecipientId id = new JceKeyAgreeRecipientId(reciCert);
+
+        Collection collection = recipients.getRecipients(id);
+        if (collection.size() != 1)
+        {
+            fail("recipients not matched using general recipient ID.");
+        }
+        assertTrue(collection.iterator().next() instanceof RecipientInformation);
+    }
+
     private void processInput(ECPrivateKey ecKey, byte[] expected, String input, AlgorithmIdentifier wrapAlg)
         throws CMSException, IOException
     {
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 7277098..b354c52 100644
--- a/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/cms/test/NewSignedDataTest.java
@@ -24,13 +24,13 @@
 import junit.framework.Test;
 import junit.framework.TestCase;
 import junit.framework.TestSuite;
+import org.bouncycastle.asn1.ASN1ApplicationSpecific;
 import org.bouncycastle.asn1.ASN1EncodableVector;
 import org.bouncycastle.asn1.ASN1Encoding;
 import org.bouncycastle.asn1.ASN1InputStream;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.DERApplicationSpecific;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSet;
@@ -41,10 +41,12 @@
 import org.bouncycastle.asn1.cms.ContentInfo;
 import org.bouncycastle.asn1.cms.SignedData;
 import org.bouncycastle.asn1.cms.SignerInfo;
+import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.ocsp.OCSPResponse;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509AttributeCertificateHolder;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaCRLStore;
@@ -566,6 +568,30 @@
             + "/JNmKBiVxuxZYYHI20CZHrgjb+ARczWuOJuBVEGEgbW/t7hMVX8X+MPAzZek"
             + "Ndi9ZfkurEeYdDpGluYBH910/P95ibG8nrJyTaoxhmOJhJ/o/SO54m8oDlI0");
 
+    private static final Set noParams = new HashSet();
+
+    static
+    {
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA1);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA224);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA256);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA384);
+        noParams.add(X9ObjectIdentifiers.ecdsa_with_SHA512);
+        noParams.add(X9ObjectIdentifiers.id_dsa_with_sha1);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha224);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
+        noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_512);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+    }
+    
     public NewSignedDataTest(String name)
     {
         super(name);
@@ -1320,6 +1346,30 @@
         }
     }
 
+    public void testRawSHA256MissingNull()
+        throws Exception
+    {
+        final byte[] document = getInput("rawsha256nonull.p7m");
+
+        final CMSSignedData s = new CMSSignedData(document);
+
+        final Store certStore = s.getCertificates();
+        final SignerInformation signerInformation = (SignerInformation)s.getSignerInfos().getSigners().iterator().next();
+
+        Collection          certCollection = certStore.getMatches(signerInformation.getSID());
+
+        Iterator        certIt = certCollection.iterator();
+        X509Certificate cert = new JcaX509CertificateConverter().setProvider("BC").getCertificate((X509CertificateHolder)certIt.next());
+
+        final SignerInformationVerifier signerInformationVerifier =
+            new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(cert.getPublicKey());
+
+        if (!signerInformation.verify(signerInformationVerifier))
+        {
+            fail("raw sig failed");
+        }
+    }
+
     public void testLwSHA1WithRSAAndAttributeTable()
         throws Exception
     {
@@ -1371,6 +1421,57 @@
         verifyRSASignatures(s, md.digest("Hello world!".getBytes()));
     }
 
+    public void testLwSHA3_256WithRSAAndAttributeTable()
+        throws Exception
+    {
+        MessageDigest       md = MessageDigest.getInstance("SHA3-256", BC);
+        List                certList = new ArrayList();
+        CMSTypedData        msg = new CMSProcessableByteArray("Hello world!".getBytes());
+
+        certList.add(_origCert);
+        certList.add(_signCert);
+
+        Store           certs = new JcaCertStore(certList);
+
+        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);
+
+        AsymmetricKeyParameter privKey = PrivateKeyFactory.createKey(_origKP.getPrivate().getEncoded());
+
+        AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA3-256withRSA");
+        AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);
+
+        BcContentSignerBuilder contentSignerBuilder = new BcRSAContentSignerBuilder(sigAlgId, digAlgId);
+
+        gen.addSignerInfoGenerator(
+            new SignerInfoGeneratorBuilder(new BcDigestCalculatorProvider())
+                .setSignedAttributeGenerator(new DefaultSignedAttributeTableGenerator(new AttributeTable(v)))
+                .build(contentSignerBuilder.build(privKey), new JcaX509CertificateHolder(_origCert)));
+
+        gen.addCertificates(certs);
+
+        CMSSignedData s = gen.generate(new CMSAbsentContent(), false);
+
+        //
+        // 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()));
+        verifyRSASignatures(s, md.digest("Hello world!".getBytes()));
+    }
+
     public void testSHA1WithRSAEncapsulated()
         throws Exception
     {
@@ -1407,6 +1508,48 @@
         rsaPSSTest("SHA384withRSAandMGF1");
     }
 
+    public void testSHA3_224WithRSAPSS()
+        throws Exception
+    {
+        rsaPSSTest("SHA3-224withRSAandMGF1");
+    }
+
+    public void testSHA3_256WithRSAPSS()
+        throws Exception
+    {
+        rsaPSSTest("SHA3-256withRSAandMGF1");
+    }
+
+    public void testSHA3_384WithRSAPSS()
+        throws Exception
+    {
+        rsaPSSTest("SHA3-384withRSAandMGF1");
+    }
+
+    public void testSHA3_224WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-224withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_224);
+    }
+
+    public void testSHA3_256WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-256withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_256);
+    }
+
+    public void testSHA3_384WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-384withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_384);
+    }
+
+    public void testSHA3_512WithDSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signDsaKP, _signDsaCert, "SHA3-512withDSA", NISTObjectIdentifiers.id_dsa_with_sha3_512);
+    }
+
     // RFC 5754 update
     public void testSHA224WithRSAEncapsulated()
         throws Exception
@@ -1421,6 +1564,30 @@
         encapsulatedTest(_signKP, _signCert, "SHA256withRSA", PKCSObjectIdentifiers.sha256WithRSAEncryption);
     }
 
+    public void testSHA3_224WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-224withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+    }
+
+    public void testSHA3_256WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-256withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+    }
+
+    public void testSHA3_384WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-384withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+    }
+
+    public void testSHA3_512WithRSAEncapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signKP, _signCert, "SHA3-512withRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
+    }
+
     public void testRIPEMD128WithRSAEncapsulated()
         throws Exception
     {
@@ -1475,6 +1642,30 @@
         encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA512withECDSA");
     }
 
+    public void testECDSASHA3_224Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-224withECDSA");
+    }
+
+    public void testECDSASHA3_256Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-256withECDSA");
+    }
+
+    public void testECDSASHA3_384Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-384withECDSA");
+    }
+
+    public void testECDSASHA3_512Encapsulated()
+        throws Exception
+    {
+        encapsulatedTest(_signEcDsaKP, _signEcDsaCert, "SHA3-512withECDSA");
+    }
+
     public void testECDSASHA512EncapsulatedWithKeyFactoryAsEC()
         throws Exception
     {
@@ -1866,7 +2057,14 @@
             if (sigAlgOid != null)
             {
                 assertEquals(sigAlgOid.getId(), signer.getEncryptionAlgOID());
-                assertEquals(DERNull.INSTANCE, ASN1Primitive.fromByteArray(signer.getEncryptionAlgParams()));
+                if (noParams.contains(sigAlgOid))
+                {
+                    assertNull(signer.getEncryptionAlgParams());
+                }
+                else
+                {
+                    assertEquals(DERNull.INSTANCE, ASN1Primitive.fromByteArray(signer.getEncryptionAlgParams()));
+                }
             }
 
             digestAlgorithms.remove(signer.getDigestAlgorithmID());
@@ -2369,7 +2567,7 @@
     public void testMixed()
         throws Exception
     {
-        DERApplicationSpecific derApplicationSpecific = (DERApplicationSpecific)ASN1Primitive.fromByteArray(mixedSignedData);
+        ASN1ApplicationSpecific derApplicationSpecific = (ASN1ApplicationSpecific)ASN1Primitive.fromByteArray(mixedSignedData);
 
         CMSSignedData s = new CMSSignedData(new ByteArrayInputStream(derApplicationSpecific.getContents()));
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java b/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java
index 90a46b4..856d39d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/dvcs/test/DVCSParseTest.java
@@ -6,7 +6,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import junit.framework.TestCase;
 import org.bouncycastle.asn1.ASN1GeneralizedTime;
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -34,9 +33,12 @@
 import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.cms.CMSSignedData;
 import org.bouncycastle.dvcs.DVCSException;
+import org.bouncycastle.util.Integers;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
 
+import junit.framework.TestCase;
+
 public class DVCSParseTest
     extends TestCase
 {
@@ -268,7 +270,7 @@
      */
     private void validate(String name, DVCSRequestInformation info, DVCSRequestInformation expected)
     {
-        validate(name + ".version", new Integer(info.getVersion()), new Integer(expected.getVersion()));
+        validate(name + ".version", Integers.valueOf(info.getVersion()), Integers.valueOf(expected.getVersion()));
         validate(name + ".service", info.getService().getValue(), expected.getService().getValue());
         validate(name + ".nonce", info.getNonce(), expected.getNonce());
         validate(name + ".requestTime", info.getRequestTime(), expected.getRequestTime());
@@ -344,7 +346,7 @@
         {
             return;
         }
-        validate(name + ".version", new Integer(result.getVersion()), new Integer(expected.getVersion()));
+        validate(name + ".version", Integers.valueOf(result.getVersion()), Integers.valueOf(expected.getVersion()));
         validate(name + ".dvReqInfo", result.getDvReqInfo(), expected.getDvReqInfo());
         validate(name + ".messageImprint", result.getMessageImprint(), expected.getMessageImprint());
         validate(name + ".serialNumber", result.getSerialNumber(), expected.getSerialNumber());
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java b/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java
index 0250983..dfa60d6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/CSRAttributesResponse.java
@@ -23,7 +23,7 @@
      * Create a CSRAttributesResponse from the passed in bytes.
      *
      * @param responseEncoding BER/DER encoding of the certificate.
-     * @throws IOException in the event of corrupted data, or an incorrect structure.
+     * @throws ESTException in the event of corrupted data, or an incorrect structure.
      */
     public CSRAttributesResponse(byte[] responseEncoding)
         throws ESTException
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java b/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java
index f9452bf..c315d23 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/ESTClient.java
@@ -6,7 +6,7 @@
  * ESTClient implement connection to the server.
  * <p>
  * Implementations should be aware that they are responsible for
- * satisfying <a hrref="https://tools.ietf.org/html/rfc7030#section-3.3">RFC7030 3.3 - TLS Layer</a>
+ * satisfying <a href="https://tools.ietf.org/html/rfc7030#section-3.3">RFC7030 3.3 - TLS Layer</a>
  * including SRP modes.
  */
 public interface ESTClient
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java b/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java
index b4b2ade..854c448 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/HttpAuth.java
@@ -21,6 +21,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.Strings;
 import org.bouncycastle.util.encoders.Base64;
 import org.bouncycastle.util.encoders.Hex;
@@ -163,10 +164,17 @@
                         {
                             throw new IllegalArgumentException("User must not contain a ':'");
                         }
-                        String userPass = username + ":" + new String(password);
-                        answer.setHeader("Authorization", "Basic " + Base64.toBase64String(userPass.getBytes()));
+                        //userPass = username + ":" + password;
+                        char[]  userPass = new char[username.length() + 1 + password.length];
+                        System.arraycopy(username.toCharArray(), 0, userPass, 0, username.length());
+                        userPass[username.length()] = ':';
+                        System.arraycopy(password, 0, userPass, username.length() + 1, password.length);
+
+                        answer.setHeader("Authorization", "Basic " + Base64.toBase64String(Strings.toByteArray(userPass)));
 
                         res = req.getClient().doRequest(answer.build());
+
+                        Arrays.fill(userPass, (char)0);
                     }
                     else
                     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java
index fe8e374..a3e8362 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/ChannelBindingProvider.java
@@ -7,8 +7,8 @@
  * Channel Binding Provider provides a method of extracting the
  * ChannelBinding that can be customised specifically for the provider.
  * Presently JSSE does not support RFC 5920.
- *
- * @See https://bugs.openjdk.java.net/browse/JDK-6491070
+ * <p>
+ * See https://bugs.openjdk.java.net/browse/JDK-6491070
  */
 public interface ChannelBindingProvider
 {
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java
index 2dec2c6..05b6a36 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/jcajce/JsseDefaultHostnameAuthorizer.java
@@ -9,6 +9,8 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 import javax.net.ssl.SSLSession;
 
@@ -18,6 +20,7 @@
 import org.bouncycastle.asn1.x500.style.BCStyle;
 import org.bouncycastle.est.ESTException;
 import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Hex;
 
 
 /**
@@ -26,6 +29,7 @@
 public class JsseDefaultHostnameAuthorizer
     implements JsseHostnameAuthorizer
 {
+    private static Logger LOG = Logger.getLogger(JsseDefaultHostnameAuthorizer.class.getName());
 
     private final Set<String> knownSuffixes;
 
@@ -84,7 +88,8 @@
                 for (Iterator it = n.iterator(); it.hasNext();)
                 {
                     List l = (List)it.next();
-                    switch (((Number)l.get(0)).intValue())
+                    int type = ((Number)l.get(0)).intValue();
+                    switch (type)
                     {
                     case 2:
                         if (isValidNameMatch(name, l.get(1).toString(), knownSuffixes))
@@ -99,7 +104,21 @@
                         }
                         break;
                     default:
-                        throw new RuntimeException("Unable to handle ");
+                        // ignore, maybe log
+                        if (LOG.isLoggable(Level.INFO))
+                        {
+                            String value;
+                            if (l.get(1) instanceof byte[])
+                            {
+                                value = Hex.toHexString((byte[])l.get(1));
+                            }
+                            else
+                            {
+                                value = l.get(1).toString();
+                            }
+
+                            LOG.log(Level.INFO, "ignoring type " + type + " value = " + value);
+                        }
                     }
                 }
 
@@ -123,7 +142,7 @@
 
         // Common Name match only.
         RDN[] rdNs = X500Name.getInstance(cert.getSubjectX500Principal().getEncoded()).getRDNs();
-        for (int i = 0; i != rdNs.length; i++)
+        for (int i = rdNs.length - 1; i >= 0; --i)
         {
             RDN rdn = rdNs[i];
             AttributeTypeAndValue[] typesAndValues = rdn.getTypesAndValues();
@@ -132,7 +151,7 @@
                 AttributeTypeAndValue atv = typesAndValues[j];
                 if (atv.getType().equals(BCStyle.CN))
                 {
-                    return isValidNameMatch(name, rdn.getFirst().getValue().toString(), knownSuffixes);
+                    return isValidNameMatch(name, atv.getValue().toString(), knownSuffixes);
                 }
             }
         }
@@ -180,7 +199,7 @@
 
                 if (wildIndex > 0)
                 {
-                    if (loweredName.startsWith(dnsName.substring(0, wildIndex - 1)) && loweredName.endsWith(end))
+                    if (loweredName.startsWith(dnsName.substring(0, wildIndex)) && loweredName.endsWith(end))
                     {
                         return loweredName.substring(wildIndex, loweredName.length() - end.length()).indexOf('.') < 0;
                     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java
index a1d50eb..34080fe 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/AllTests.java
@@ -35,6 +35,7 @@
 
         suite.addTestSuite(ESTParsingTest.class);
         suite.addTestSuite(HostNameAuthorizerMatchTest.class);
+        suite.addTestSuite(TestHostNameAuthorizer.class);
 
         return new ESTTestSetup(suite);
     }
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java b/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java
index 32b0618..5614072 100644
--- a/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/HostNameAuthorizerMatchTest.java
@@ -32,6 +32,9 @@
             {"Invalid 13", "foo.example.com","*.example.com",true},
             {"Invalid 14", "bar.foo.example.com", "*.example.com", false},
             {"Invalid 15", "example.com", "*.example.com", false},
+            {"Invalid 16", "foobaz.example.com","b*z.example.com",false},
+            {"Invalid 17", "foobaz.example.com","ob*z.example.com",false},
+            { "Valid", "foobaz.example.com","foob*z.example.com",true}
         };
 
         for (Object[] j : v)
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/TestHostNameAuthorizer.java b/bcpkix/src/main/java/org/bouncycastle/est/test/TestHostNameAuthorizer.java
new file mode 100644
index 0000000..3a2792a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/TestHostNameAuthorizer.java
@@ -0,0 +1,107 @@
+package org.bouncycastle.est.test;
+
+
+import java.io.InputStreamReader;
+import java.security.cert.X509Certificate;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
+import org.bouncycastle.est.jcajce.JsseDefaultHostnameAuthorizer;
+import org.bouncycastle.util.io.pem.PemReader;
+
+/**
+ * TestHostNameAuthorizer tests the hostname authorizer only. EST related functions
+ * are not tested here.
+ */
+public class TestHostNameAuthorizer
+    extends TestCase
+{
+    private static X509Certificate readPemCertificate(String path)
+        throws Exception
+    {
+        InputStreamReader fr = new InputStreamReader(TestHostNameAuthorizer.class.getResourceAsStream(path));
+        PemReader reader = new PemReader(fr);
+        X509CertificateHolder fromFile = new X509CertificateHolder(reader.readPemObject().getContent());
+        reader.close();
+        fr.close();
+        return new JcaX509CertificateConverter().setProvider("BC").getCertificate(fromFile);
+    }
+
+    /*
+        The following tests do not attempt to validate the certificates.
+        They only test hostname verification behavior.
+     */
+    public void testCNMatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_match_wc.pem");
+
+        assertTrue("Common Name match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark.cisco.com", cert));
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("cisco.com", cert));
+    }
+
+    public void testCNMismatch_1()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_mismatch_wc.pem");
+
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark", cert));
+    }
+
+
+    // 192.168.1.50
+    public void testCNIPMismatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_mismatch_ip.pem");
+
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("127.0.0.1", cert));
+    }
+
+    public void testWCMismatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_cn_mismatch_ip.pem");
+
+        assertFalse("Not match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark.cisco.com", cert));
+    }
+
+    public void testSANMatch()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_match.pem");
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("localhost.cisco.com", cert));
+    }
+
+    public void testSANMatchIP()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_match_ip.pem");
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("192.168.51.140", cert));
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("127.0.0.1", cert));
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("10.0.0.1", cert));
+    }
+
+    public void testSANMatchWC()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_mismatch_wc.pem");
+        assertTrue("Match", new JsseDefaultHostnameAuthorizer(null).verify("roundhouse.yahoo.com", cert));
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("aardvark.cisco.com", cert));
+    }
+
+    public void testSANMismatchIP()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_mismatch_ip.pem");
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("localhost.me", cert));
+    }
+
+    public void testSANMismatchWC()
+        throws Exception
+    {
+        X509Certificate cert = readPemCertificate("san/cert_san_mismatch_wc.pem");
+        assertFalse("Not Match", new JsseDefaultHostnameAuthorizer(null).verify("localhost.me", cert));
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_match_wc.pem
new file mode 100644
index 0000000..0860f79
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_match_wc.pem
@@ -0,0 +1,44 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 865 (0x361)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:41:31 2014 GMT
+            Not After : Dec 16 12:41:31 2022 GMT
+        Subject: CN=*.cisco.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:b7:08:e6:18:f2:32:d7:07:44:4b:f3:b1:83:01:
+                    59:f8:bc:ec:26:71:92:9a:53:70:f2:c0:be:2a:d6:
+                    26:6f:45:11:86:d7:ee:37:9d:d3:2f:22:b2:8b:9b:
+                    c5:96:00:36:73:97:c3:4c:f2:7a:0b:2c:e0:cc:d9:
+                    f0:ec:ba:1b:75:8c:66:b1:86:10:fd:be:df:6b:67:
+                    9c:0e:6b:2a:0e:d0:80:a8:dc:7a:d4:df:6e:79:28:
+                    a7:60:1a:11:b7:ae:40:94:bb:b4:11:ed:1b:6f:a7:
+                    91:ae:33:ec:bf:9c:30:f3:dc:91:2c:b4:3e:8c:c9:
+                    bd:f1:d1:aa:f6:c2:1d:6a:cd
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:76:4f:3a:6c:b4:99:cb:1e:37:f4:0d:6e:e1:74:
+         4b:99:bb:f5:c4:b6:3d:c1:61:df:8c:d7:1f:9f:e7:d3:64:d6:
+         02:20:64:38:8f:6f:32:37:2b:7d:cf:28:93:e5:e6:e7:70:c5:
+         a9:12:04:b0:4b:a5:29:7b:23:df:85:f2:18:44:8b:d2
+-----BEGIN CERTIFICATE-----
+MIIBezCCASOgAwIBAgICA2EwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTI0MTMxWhcNMjIxMjE2MTI0MTMxWjAWMRQwEgYDVQQD
+DAsqLmNpc2NvLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAtwjmGPIy
+1wdES/OxgwFZ+LzsJnGSmlNw8sC+KtYmb0URhtfuN53TLyKyi5vFlgA2c5fDTPJ6
+CyzgzNnw7LobdYxmsYYQ/b7fa2ecDmsqDtCAqNx61N9ueSinYBoRt65AlLu0Ee0b
+b6eRrjPsv5ww89yRLLQ+jMm98dGq9sIdas0CAwEAAaMaMBgwCQYDVR0TBAIwADAL
+BgNVHQ8EBAMCBeAwCQYHKoZIzj0EAQNHADBEAiB2TzpstJnLHjf0DW7hdEuZu/XE
+tj3BYd+M1x+f59Nk1gIgZDiPbzI3K33PKJPl5udwxakSBLBLpSl7I9+F8hhEi9I=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch.pem
new file mode 100644
index 0000000..5a0b3d3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch.pem
@@ -0,0 +1,44 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 863 (0x35f)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:36:22 2014 GMT
+            Not After : Dec 16 12:36:22 2022 GMT
+        Subject: CN=hostname
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c2:4e:9f:27:15:91:6b:2b:e7:85:a8:50:d9:5b:
+                    1a:a9:23:0a:84:1c:fd:e7:24:dc:29:18:f2:52:55:
+                    43:25:e4:3e:ce:02:51:9c:93:19:67:89:c9:93:6d:
+                    dc:5d:56:ad:cb:b0:7e:2c:7a:ad:98:17:7f:bb:19:
+                    62:7d:2e:f0:0b:cf:c1:18:6f:6f:3a:fc:3d:3c:03:
+                    9b:18:66:5f:dc:2a:fa:72:54:bf:5f:b0:75:dd:bf:
+                    84:40:b1:3a:c5:65:2d:84:ee:48:76:1d:45:fa:1d:
+                    e2:b2:25:5e:aa:06:8c:11:66:ef:40:f0:68:14:08:
+                    a8:7e:62:4a:d2:e9:88:bd:3d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:45:02:21:00:a8:bd:82:16:2c:9c:bf:77:1a:4d:fc:0f:a5:
+         a6:da:6e:e7:2f:45:fc:58:be:e3:0c:d2:a7:36:41:1f:45:c0:
+         80:02:20:6f:82:eb:4b:05:63:c9:e3:c7:f8:42:c0:ff:f1:0f:
+         5f:95:db:95:6e:71:fb:05:f0:52:e0:a6:82:53:45:f6:e3
+-----BEGIN CERTIFICATE-----
+MIIBeTCCASCgAwIBAgICA18wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTIzNjIyWhcNMjIxMjE2MTIzNjIyWjATMREwDwYDVQQD
+DAhob3N0bmFtZTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwk6fJxWRayvn
+hahQ2VsaqSMKhBz95yTcKRjyUlVDJeQ+zgJRnJMZZ4nJk23cXVaty7B+LHqtmBd/
+uxlifS7wC8/BGG9vOvw9PAObGGZf3Cr6clS/X7B13b+EQLE6xWUthO5Idh1F+h3i
+siVeqgaMEWbvQPBoFAiofmJK0umIvT0CAwEAAaMaMBgwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwCQYHKoZIzj0EAQNIADBFAiEAqL2CFiycv3caTfwPpababucvRfxY
+vuMM0qc2QR9FwIACIG+C60sFY8njx/hCwP/xD1+V25VucfsF8FLgpoJTRfbj
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_ip.pem
new file mode 100644
index 0000000..65b7a24
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_ip.pem
@@ -0,0 +1,45 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 864 (0x360)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:39:24 2014 GMT
+            Not After : Dec 16 12:39:24 2022 GMT
+        Subject: CN=192.168.1.50
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:d8:fe:96:35:15:34:48:a5:6c:21:65:8e:0b:9f:
+                    85:59:2e:24:f6:9b:23:2a:d9:d3:71:92:b3:24:2d:
+                    1f:ae:f5:bb:1b:84:e9:ed:42:8a:b9:47:bc:92:70:
+                    69:93:a7:c8:50:4b:05:89:36:67:34:b4:2a:97:fb:
+                    64:9e:49:19:68:0d:21:36:36:63:6f:df:d9:39:f7:
+                    e9:da:ff:fe:9a:a8:e6:d5:75:bb:3f:e5:38:f5:c2:
+                    26:f4:f1:f4:b6:5c:9b:a7:4b:2c:7d:34:ff:c0:87:
+                    ad:dc:2c:6a:bd:22:cc:13:78:ff:f5:93:c7:63:10:
+                    44:e0:3f:2c:04:91:26:9b:eb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:45:02:21:00:b9:1f:ee:63:e9:0a:79:a6:76:72:e8:d8:93:
+         b8:26:aa:ff:15:04:2b:f0:37:bb:45:96:5b:0b:ce:15:67:b7:
+         75:02:20:21:62:07:24:76:f4:98:90:f4:6d:7e:d7:57:62:a6:
+         6a:b1:40:b7:d2:73:1c:58:24:eb:a9:3a:19:90:34:0e:ba
+-----BEGIN CERTIFICATE-----
+MIIBfTCCASSgAwIBAgICA2AwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTIzOTI0WhcNMjIxMjE2MTIzOTI0WjAXMRUwEwYDVQQD
+DAwxOTIuMTY4LjEuNTAwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBANj+ljUV
+NEilbCFljgufhVkuJPabIyrZ03GSsyQtH671uxuE6e1CirlHvJJwaZOnyFBLBYk2
+ZzS0Kpf7ZJ5JGWgNITY2Y2/f2Tn36dr//pqo5tV1uz/lOPXCJvTx9LZcm6dLLH00
+/8CHrdwsar0izBN4//WTx2MQROA/LASRJpvrAgMBAAGjGjAYMAkGA1UdEwQCMAAw
+CwYDVR0PBAQDAgXgMAkGByqGSM49BAEDSAAwRQIhALkf7mPpCnmmdnLo2JO4Jqr/
+FQQr8De7RZZbC84VZ7d1AiAhYgckdvSYkPRtftdXYqZqsUC30nMcWCTrqToZkDQO
+ug==
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_wc.pem
new file mode 100644
index 0000000..827c71c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_cn_mismatch_wc.pem
@@ -0,0 +1,44 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 867 (0x363)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 29 12:43:07 2014 GMT
+            Not After : Dec 16 12:43:07 2022 GMT
+        Subject: CN=*.google.com
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c0:4a:fd:4b:ac:bc:cd:ca:d2:7d:ba:03:d4:49:
+                    5e:68:fb:47:dd:f5:01:9c:ec:65:82:7f:50:9f:24:
+                    3f:9f:44:96:14:d5:9c:64:a9:19:51:83:2f:5e:62:
+                    11:a0:46:14:1f:e8:d9:c4:61:23:6b:fe:96:59:a3:
+                    cd:e5:c3:82:08:c4:3f:55:a4:5c:7a:63:9d:bb:58:
+                    ec:79:62:31:c2:4d:c4:1d:43:05:bc:09:78:a1:c1:
+                    27:21:41:b7:03:82:11:96:5d:b5:97:92:a1:93:f8:
+                    1c:e2:5f:33:e6:03:0e:03:9e:84:6a:72:d6:00:9f:
+                    77:75:2d:be:e6:84:fb:22:b3
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:09:d4:15:d9:f2:48:dd:be:68:6f:1a:dd:48:fb:
+         85:e3:f3:e4:f8:67:a6:36:fc:0f:b2:bb:23:f7:ba:92:77:bc:
+         02:20:4f:aa:2f:29:1f:df:4f:0e:fa:fe:57:6e:85:5e:30:bd:
+         21:56:c0:ef:30:be:7b:48:6a:f1:71:46:f2:17:fe:b6
+-----BEGIN CERTIFICATE-----
+MIIBfDCCASSgAwIBAgICA2MwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTQwOTI5MTI0MzA3WhcNMjIxMjE2MTI0MzA3WjAXMRUwEwYDVQQD
+DAwqLmdvb2dsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMBK/Uus
+vM3K0n26A9RJXmj7R931AZzsZYJ/UJ8kP59ElhTVnGSpGVGDL15iEaBGFB/o2cRh
+I2v+llmjzeXDggjEP1WkXHpjnbtY7HliMcJNxB1DBbwJeKHBJyFBtwOCEZZdtZeS
+oZP4HOJfM+YDDgOehGpy1gCfd3UtvuaE+yKzAgMBAAGjGjAYMAkGA1UdEwQCMAAw
+CwYDVR0PBAQDAgXgMAkGByqGSM49BAEDRwAwRAIgCdQV2fJI3b5obxrdSPuF4/Pk
++GemNvwPsrsj97qSd7wCIE+qLykf308O+v5XboVeML0hVsDvML57SGrxcUbyF/62
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match.pem
new file mode 100644
index 0000000..8c3ce2f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 687 (0x2af)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 13:55:10 2013 GMT
+            Not After : Nov  5 13:55:10 2017 GMT
+        Subject: CN=tester
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ee:44:f3:93:2c:a6:3f:9c:ba:2a:83:b0:bd:a5:
+                    74:a5:c8:d3:a8:a0:44:4d:eb:ab:f5:4d:72:5a:d8:
+                    6f:ce:74:bb:4a:d1:8a:a8:9a:e5:e2:a7:0c:95:c7:
+                    a0:7d:c8:84:6f:63:b2:c3:09:f4:ea:0c:06:f7:99:
+                    e2:0e:c3:f0:cf:44:30:33:08:f8:69:79:7a:63:34:
+                    6d:ed:a9:cf:f7:9b:ca:dd:24:25:cd:bc:0e:cc:17:
+                    cc:1f:8c:1c:15:7b:5f:ca:7e:26:15:dc:a5:54:7e:
+                    9c:47:46:59:1e:80:f0:37:e4:c3:d7:96:df:48:d2:
+                    a7:14:d8:8f:7a:78:ee:f3:5d
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:localhost.cisco.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:46:02:21:00:97:1b:17:9e:9e:df:8f:fb:d2:18:5f:89:d8:
+         30:31:87:9c:f8:56:9b:6d:e1:b6:87:bf:c7:d6:c3:a9:ab:f3:
+         6c:02:21:00:fb:40:3e:6a:9a:72:d8:ac:95:b5:62:39:22:69:
+         34:61:ad:55:0f:e8:5f:51:08:48:24:53:b7:5c:72:cc:5d:ae
+-----BEGIN CERTIFICATE-----
+MIIBmDCCAT6gAwIBAgICAq8wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTM1NTEwWhcNMTcxMTA1MTM1NTEwWjARMQ8wDQYDVQQD
+DAZ0ZXN0ZXIwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAO5E85Mspj+cuiqD
+sL2ldKXI06igRE3rq/VNclrYb850u0rRiqia5eKnDJXHoH3IhG9jssMJ9OoMBveZ
+4g7D8M9EMDMI+Gl5emM0be2pz/ebyt0kJc28DswXzB+MHBV7X8p+JhXcpVR+nEdG
+WR6A8Dfkw9eW30jSpxTYj3p47vNdAgMBAAGjOjA4MAkGA1UdEwQCMAAwCwYDVR0P
+BAQDAgXgMB4GA1UdEQQXMBWCE2xvY2FsaG9zdC5jaXNjby5jb20wCQYHKoZIzj0E
+AQNJADBGAiEAlxsXnp7fj/vSGF+J2DAxh5z4Vptt4baHv8fWw6mr82wCIQD7QD5q
+mnLYrJW1YjkiaTRhrVUP6F9RCEgkU7dccsxdrg==
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_ip.pem
new file mode 100644
index 0000000..dd676b7
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_ip.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 717 (0x2cd)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 15:00:41 2013 GMT
+            Not After : Nov  5 15:00:41 2017 GMT
+        Subject: CN=tester13
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c2:04:ef:f9:51:a9:a6:72:ad:23:e7:af:7d:51:
+                    8b:dc:7e:d0:71:33:5d:93:65:52:6e:79:df:30:58:
+                    cf:08:4e:47:8d:4a:1f:6a:ea:48:67:68:27:10:0d:
+                    a8:c2:80:e8:0d:10:12:34:32:e9:d8:77:e5:15:ec:
+                    9c:95:ba:f4:16:57:e5:aa:6f:18:9e:ee:4e:6c:ef:
+                    4c:f3:bf:91:f0:4b:b0:8d:c0:ff:8f:6e:f1:f3:b9:
+                    31:8e:73:d9:5c:c5:5f:fd:30:ae:36:ba:80:aa:75:
+                    ae:4d:50:3b:ba:b3:9a:a6:d2:3a:45:20:c3:7e:86:
+                    79:af:74:d7:c2:d4:c1:d4:fb
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                IP Address:192.168.51.140, IP Address:127.0.0.1
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:6e:64:22:3a:61:01:a6:bb:5a:3d:52:12:4e:b0:
+         14:50:34:6d:0d:44:ba:28:24:88:50:a8:3a:45:01:76:1e:3a:
+         02:20:5e:8f:cf:9b:74:e8:a3:29:7c:bb:15:2b:34:14:7a:ad:
+         1e:98:07:2a:cf:75:88:88:45:51:d7:2c:98:a0:4c:01
+-----BEGIN CERTIFICATE-----
+MIIBjzCCATegAwIBAgICAs0wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTUwMDQxWhcNMTcxMTA1MTUwMDQxWjATMREwDwYDVQQD
+DAh0ZXN0ZXIxMzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwgTv+VGppnKt
+I+evfVGL3H7QcTNdk2VSbnnfMFjPCE5HjUofaupIZ2gnEA2owoDoDRASNDLp2Hfl
+Feyclbr0Flflqm8Ynu5ObO9M87+R8EuwjcD/j27x87kxjnPZXMVf/TCuNrqAqnWu
+TVA7urOaptI6RSDDfoZ5r3TXwtTB1PsCAwEAAaMxMC8wCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwFQYDVR0RBA4wDIcEwKgzjIcEfwAAATAJBgcqhkjOPQQBA0cAMEQC
+IG5kIjphAaa7Wj1SEk6wFFA0bQ1EuigkiFCoOkUBdh46AiBej8+bdOijKXy7FSs0
+FHqtHpgHKs91iIhFUdcsmKBMAQ==
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_wc.pem
new file mode 100644
index 0000000..1a267de
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_match_wc.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 719 (0x2cf)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 15:10:16 2013 GMT
+            Not After : Nov  5 15:10:16 2017 GMT
+        Subject: CN=tester14
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:ae:3a:26:93:ee:cb:de:f6:5b:b5:fb:a3:2e:b6:
+                    2b:cc:eb:49:1a:76:38:60:30:eb:7f:9a:99:f5:d2:
+                    7f:fd:c8:da:bd:f3:38:4b:7d:27:fb:15:81:23:59:
+                    22:92:1f:ad:77:f7:5f:50:e7:69:e2:64:2a:68:33:
+                    63:62:02:10:fa:2c:e1:06:ae:cc:04:79:e5:8c:1f:
+                    ea:9a:26:9e:03:b0:4f:b4:f8:1c:2b:21:6b:b0:6d:
+                    68:24:ae:0c:2e:a7:36:7b:27:d4:f1:13:ef:75:5e:
+                    e2:11:49:fc:d4:f8:4d:2d:63:3e:c8:08:44:a3:c8:
+                    79:42:4e:85:a5:5e:ab:91:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:*.cisco.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:47:98:0f:e8:c3:0f:b1:8e:80:bc:ec:66:30:da:
+         68:0d:2e:c3:de:aa:20:66:e8:8a:dd:d0:12:97:dd:67:af:0d:
+         02:20:6f:8a:1a:37:f4:19:ce:5d:8c:5e:af:b2:e7:7d:72:5b:
+         7e:dc:a9:7e:49:ec:bf:c4:d5:5b:38:5b:9e:b2:1e:27
+-----BEGIN CERTIFICATE-----
+MIIBkDCCATigAwIBAgICAs8wCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTUxMDE2WhcNMTcxMTA1MTUxMDE2WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxNDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEArjomk+7L3vZb
+tfujLrYrzOtJGnY4YDDrf5qZ9dJ//cjavfM4S30n+xWBI1kikh+td/dfUOdp4mQq
+aDNjYgIQ+izhBq7MBHnljB/qmiaeA7BPtPgcKyFrsG1oJK4MLqc2eyfU8RPvdV7i
+EUn81PhNLWM+yAhEo8h5Qk6FpV6rkVMCAwEAAaMyMDAwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwFgYDVR0RBA8wDYILKi5jaXNjby5jb20wCQYHKoZIzj0EAQNHADBE
+AiBHmA/oww+xjoC87GYw2mgNLsPeqiBm6Ird0BKX3WevDQIgb4oaN/QZzl2MXq+y
+531yW37cqX5J7L/E1Vs4W56yHic=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch.pem
new file mode 100644
index 0000000..2c9f5d2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 715 (0x2cb)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 14:35:46 2013 GMT
+            Not After : Nov  5 14:35:46 2017 GMT
+        Subject: CN=tester11
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:bd:98:4d:6e:91:3f:05:65:cf:ee:aa:24:16:b3:
+                    44:ba:d8:89:b1:c9:bd:10:0a:70:92:99:de:ea:63:
+                    04:ec:d5:e3:d5:3d:41:20:f7:ab:00:cc:21:a3:b9:
+                    bf:88:83:3d:14:f4:a9:84:95:23:6c:98:d5:d3:90:
+                    1f:cb:c0:6c:90:71:57:fd:39:e6:e4:99:29:83:d6:
+                    28:58:d3:ca:3f:70:06:a4:f0:25:82:b1:73:4e:03:
+                    a6:d9:51:cd:6b:70:c6:a9:4c:75:36:4e:eb:37:46:
+                    7c:1c:2e:cf:9b:be:e5:a2:0e:1b:21:74:e8:72:a2:
+                    ad:ed:b1:3c:a0:4a:d4:c3:17
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:roundhouse.cisco.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:28:51:20:46:6e:43:bc:37:f4:83:17:30:bd:04:
+         e1:6f:3c:e8:91:63:6e:d9:d2:24:79:c5:2d:3c:0c:c0:92:ed:
+         02:20:5e:e3:2c:6c:16:66:d1:dd:1d:f5:14:a1:bb:e7:54:55:
+         fa:e9:fd:76:a5:6a:f0:56:2d:13:27:c0:c6:4e:3b:0f
+-----BEGIN CERTIFICATE-----
+MIIBmTCCAUGgAwIBAgICAsswCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTQzNTQ2WhcNMTcxMTA1MTQzNTQ2WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxMTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAvZhNbpE/BWXP
+7qokFrNEutiJscm9EApwkpne6mME7NXj1T1BIPerAMwho7m/iIM9FPSphJUjbJjV
+05Afy8BskHFX/Tnm5Jkpg9YoWNPKP3AGpPAlgrFzTgOm2VHNa3DGqUx1Nk7rN0Z8
+HC7Pm77log4bIXTocqKt7bE8oErUwxcCAwEAAaM7MDkwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwHwYDVR0RBBgwFoIUcm91bmRob3VzZS5jaXNjby5jb20wCQYHKoZI
+zj0EAQNHADBEAiAoUSBGbkO8N/SDFzC9BOFvPOiRY27Z0iR5xS08DMCS7QIgXuMs
+bBZm0d0d9RShu+dUVfrp/XalavBWLRMnwMZOOw8=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_ip.pem
new file mode 100644
index 0000000..a804944
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_ip.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 716 (0x2cc)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 14:56:29 2013 GMT
+            Not After : Nov  5 14:56:29 2017 GMT
+        Subject: CN=tester12
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:c6:65:bf:f2:9f:fd:67:96:f9:f8:69:6b:83:c8:
+                    c6:d9:a8:53:03:0e:b5:7c:79:f9:83:05:6f:60:d8:
+                    0c:ec:33:b7:2a:95:48:d2:eb:d0:ba:cd:de:0c:71:
+                    ec:c6:ef:ab:ea:4e:d0:4d:46:e1:d0:4d:9d:4c:31:
+                    40:69:09:02:d1:66:0c:c2:be:6b:e5:ea:f5:15:38:
+                    16:b2:34:20:8d:19:ee:61:b0:4c:d3:59:ec:c3:64:
+                    fd:36:54:e3:49:2f:ee:8b:e3:06:42:ee:d7:af:5d:
+                    31:6c:43:c6:b4:41:40:dc:e2:b3:ed:f6:95:f1:9c:
+                    ec:72:d9:9c:07:af:32:cc:41
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                IP Address:192.168.50.65
+    Signature Algorithm: ecdsa-with-SHA1
+         30:46:02:21:00:ef:13:e8:c2:e0:30:5a:5f:93:41:7d:14:f1:
+         d0:c1:a9:44:d3:11:72:52:c1:6c:b1:22:12:09:d8:96:0b:fa:
+         1f:02:21:00:d3:74:15:65:fc:dc:d9:0f:e8:1c:4d:1d:b3:2e:
+         37:78:47:d0:69:95:9b:bc:a2:b5:c7:4f:32:0d:50:84:45:34
+-----BEGIN CERTIFICATE-----
+MIIBizCCATGgAwIBAgICAswwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTQ1NjI5WhcNMTcxMTA1MTQ1NjI5WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxMjCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAxmW/8p/9Z5b5
++Glrg8jG2ahTAw61fHn5gwVvYNgM7DO3KpVI0uvQus3eDHHsxu+r6k7QTUbh0E2d
+TDFAaQkC0WYMwr5r5er1FTgWsjQgjRnuYbBM01nsw2T9NlTjSS/ui+MGQu7Xr10x
+bEPGtEFA3OKz7faV8ZzsctmcB68yzEECAwEAAaMrMCkwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwDwYDVR0RBAgwBocEwKgyQTAJBgcqhkjOPQQBA0kAMEYCIQDvE+jC
+4DBaX5NBfRTx0MGpRNMRclLBbLEiEgnYlgv6HwIhANN0FWX83NkP6BxNHbMuN3hH
+0GmVm7yitcdPMg1QhEU0
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_wc.pem
new file mode 100644
index 0000000..774c5d5
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/cert_san_mismatch_wc.pem
@@ -0,0 +1,47 @@
+Certificate:
+    Data:
+        Version: 3 (0x2)
+        Serial Number: 721 (0x2d1)
+    Signature Algorithm: ecdsa-with-SHA1
+        Issuer: CN=estExampleCA
+        Validity
+            Not Before: Sep 27 15:15:18 2013 GMT
+            Not After : Nov  5 15:15:18 2017 GMT
+        Subject: CN=tester15
+        Subject Public Key Info:
+            Public Key Algorithm: rsaEncryption
+                Public-Key: (1024 bit)
+                Modulus:
+                    00:a0:42:6b:73:58:0c:f2:85:6e:af:50:b3:19:44:
+                    a1:44:03:15:2b:e2:f5:0b:34:20:a3:c8:f2:d5:03:
+                    9c:5f:80:f7:ed:29:f5:9e:d4:0e:5f:64:99:a2:f1:
+                    dd:8e:d1:5b:99:85:48:1a:6f:7d:1d:50:d3:13:7b:
+                    48:df:2c:60:62:4b:5a:8b:19:2f:00:c3:b3:09:1c:
+                    09:51:27:eb:ab:ed:fb:06:62:31:69:fb:1e:f9:11:
+                    12:75:dd:a4:f9:8a:99:e9:f4:48:96:db:89:b8:64:
+                    fc:55:7f:5f:4f:1b:89:07:0c:05:b8:aa:4d:c6:e6:
+                    41:ee:7a:c3:f4:25:93:65:53
+                Exponent: 65537 (0x10001)
+        X509v3 extensions:
+            X509v3 Basic Constraints: 
+                CA:FALSE
+            X509v3 Key Usage: 
+                Digital Signature, Non Repudiation, Key Encipherment
+            X509v3 Subject Alternative Name: 
+                DNS:*.yahoo.com
+    Signature Algorithm: ecdsa-with-SHA1
+         30:44:02:20:20:9c:0e:e3:29:89:f4:2d:07:c5:66:52:9a:9a:
+         56:10:30:03:b1:9c:a3:aa:fd:2a:d2:d9:c7:67:1e:4b:f8:6a:
+         02:20:12:01:05:2b:73:8d:2b:aa:d3:5d:9e:f5:e5:4e:c8:a8:
+         eb:86:6e:02:95:f4:7d:57:d7:69:39:d8:67:7b:8b:1f
+-----BEGIN CERTIFICATE-----
+MIIBkDCCATigAwIBAgICAtEwCQYHKoZIzj0EATAXMRUwEwYDVQQDEwxlc3RFeGFt
+cGxlQ0EwHhcNMTMwOTI3MTUxNTE4WhcNMTcxMTA1MTUxNTE4WjATMREwDwYDVQQD
+DAh0ZXN0ZXIxNTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAoEJrc1gM8oVu
+r1CzGUShRAMVK+L1CzQgo8jy1QOcX4D37Sn1ntQOX2SZovHdjtFbmYVIGm99HVDT
+E3tI3yxgYktaixkvAMOzCRwJUSfrq+37BmIxafse+RESdd2k+YqZ6fRIltuJuGT8
+VX9fTxuJBwwFuKpNxuZB7nrD9CWTZVMCAwEAAaMyMDAwCQYDVR0TBAIwADALBgNV
+HQ8EBAMCBeAwFgYDVR0RBA8wDYILKi55YWhvby5jb20wCQYHKoZIzj0EAQNHADBE
+AiAgnA7jKYn0LQfFZlKamlYQMAOxnKOq/SrS2cdnHkv4agIgEgEFK3ONK6rTXZ71
+5U7IqOuGbgKV9H1X12k52Gd7ix8=
+-----END CERTIFICATE-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_match_wc.pem
new file mode 100644
index 0000000..52cdb17
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_match_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC3COYY8jLXB0RL87GDAVn4vOwmcZKaU3DywL4q1iZvRRGG1+43
+ndMvIrKLm8WWADZzl8NM8noLLODM2fDsuht1jGaxhhD9vt9rZ5wOayoO0ICo3HrU
+3255KKdgGhG3rkCUu7QR7Rtvp5GuM+y/nDDz3JEstD6Myb3x0ar2wh1qzQIDAQAB
+AoGBAKMOA2Wvxci3SYlb9AQf3RXwv3NT8+UVdmZbfE3O7vhrED0O+cGEENFJ2ML0
+JyYE1H1PcoWG8WApP65ebPmGpdQkSK+/AOp+FV2mM4/PYPoyLDTtt2qQuQj78jdv
+jE1ePtB5hQPgL8ab8sfLRJDubfruPXaqEvbJd/6aMfBE5oThAkEA6ckLfTsnOn3G
+JEAv8Ym4DG6q+VJFNLEFKDZuaxCnQP+hYdfhRNTRnIK/2GtWlP4/NSE6d/S9Vyzl
+E5B8nHEICQJBAMhtUZn3VgB8cmWBzn4c6f60+ukN9ic1toYw5t8jPo2IuivX7j+B
+jOvu22xqj1xCisliCtYi6b3TFKtnc3BmlaUCQQCFUk+pBRjj9GIQvkIZHo7FGD+M
+m2w4FlN4kUH68K5RFPb1k2U2GZ/H/5BkXSItKajmJaLwUbPAiSvCMn29wX/xAkBu
+5C0V5sbqlfAlQWCiXhUJG9EHSPY8U3edX0kdhD6DyHZY86uZ72+sygcVQQ/4l8h6
+C4i7Wa3BnRv+icpREjERAkA0nZPIl+AvS+dHMYSa2Khysy/ikYpQZPqx85f/phD9
+cJVSBV6U8kR9FzEqHI4GmUMSwFex1JVxe8u29+piocA/
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch.pem
new file mode 100644
index 0000000..4a76c85
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXwIBAAKBgQDCTp8nFZFrK+eFqFDZWxqpIwqEHP3nJNwpGPJSVUMl5D7OAlGc
+kxlnicmTbdxdVq3LsH4seq2YF3+7GWJ9LvALz8EYb286/D08A5sYZl/cKvpyVL9f
+sHXdv4RAsTrFZS2E7kh2HUX6HeKyJV6qBowRZu9A8GgUCKh+YkrS6Yi9PQIDAQAB
+AoGBAJ8chpMYxEXAZAw88keqpMBP43Kf0wKOWoKE3RmynBPRPeSwXWgbxLfWSuyh
+D8yXCCY91nRR2QksZPovJUlhQErdac3DtD991ucZUoVuY9hJXnKJ2PKbyc5sWtwj
+8u9Uj4XHukk+NPgAAyGfg3qREVFU+Cs8TBTqbQOofAjFvcM5AkEA8R31DCCsuan6
+66/tJbbgwGmnhh7NmUSfGM/ayb0fPAhhvy+MdGXlV4BjBOswc++3FsUsdPdOhaDH
+Hvk9Vv/HqwJBAM5M/dRQXNl0yXlDFyzPTZK8sq/3b0YZ2bZHNt2j0ID69RGd6+//
+qMC4msUudWahMBrJpz3ImHMd724jTB8JBrcCQQDA5DNesWEc4wQuur4gNs5Uf/Ga
+FVhrbf9NLTOdwqOTNi1kcpiNodq4hqgwjipkBXjWSYUISbRSqM3DVKciy1sjAkEA
+kAU/ztCrgUP+wyELNMUkKqSBOkdK721sh8v/iYurK+AG5l2RMOMSNDisE6vzWcp9
+grX0gzVe+D6VwBX0NlZe0QJBAJbLKGVhThv0XxkocM8g2wnbE+z3VZrcY93WuLuD
+WxOe2/JDbI9KeXbUMdeTAq89nVicv3JiQaYD/ZzhPnQJvb4=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_ip.pem
new file mode 100644
index 0000000..0433f9e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_ip.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDY/pY1FTRIpWwhZY4Ln4VZLiT2myMq2dNxkrMkLR+u9bsbhOnt
+Qoq5R7yScGmTp8hQSwWJNmc0tCqX+2SeSRloDSE2NmNv39k59+na//6aqObVdbs/
+5Tj1wib08fS2XJunSyx9NP/Ah63cLGq9IswTeP/1k8djEETgPywEkSab6wIDAQAB
+AoGAdbXNB6ij/xB1UryDTHzRWmo0tO29Kv6Uu9RHh0VPHZrBUBFO0Fy7YfyvJ4UB
+UI7AlAXOT/uKCsX9IQrHLzIaLB5SWb0tpCsCjB8++SQeIfKdpcNAyAhR3w9qQu2w
+c+d3AepsFeHwcSrqoeLAdg3/qfY5MMD6AFyI3+Xfjr27tUECQQD7GiRwUCJMd5dW
+P/kiU95MNuKg5N756QUSX3xRAjInQSJrZdILmQWITeUtdDROdtnmtOAHWK0YSVb9
+b0+N4DXhAkEA3Tohd8qbcvF63pcFMWW1D+gid1njEWQ1QktGQFvN1qP3OMAp+hxJ
+W3KCNWgdtIdJNnHGX4suStAlwZnDhxszSwJAZjkmTHLTA75L5dj1W3w5K13MtSN3
+gtXSMsCco335XPGvSXmSIRaSogLUIcUE5kyMONe5vEPlc9WnjFUcVe3JAQJAX7Vj
+f1DfObYxIxWRAJLw52XVa28u5npE9F5ekT9maQLc1OeGAZe1QOPkYzidCVoyGWTV
+nsY3C9TLUNu2FMB2ywJBAIEbxQzQ0C/FnZ6mt1IZOBvatdxbqhQQx8H2Lued1Jh5
+zkxLQBunmCxZkZgA4oGZnX3s2CjKJWHhib7zXnd0Ygc=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_wc.pem
new file mode 100644
index 0000000..c98eed2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_cn_mismatch_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDASv1LrLzNytJ9ugPUSV5o+0fd9QGc7GWCf1CfJD+fRJYU1Zxk
+qRlRgy9eYhGgRhQf6NnEYSNr/pZZo83lw4IIxD9VpFx6Y527WOx5YjHCTcQdQwW8
+CXihwSchQbcDghGWXbWXkqGT+BziXzPmAw4DnoRqctYAn3d1Lb7mhPsiswIDAQAB
+AoGBAIdbQflm2nAx+QEvU0q3apGTb/85W61OahX18JZVOcOuz8ZSpwfkUEIkic9y
+UN309m2PxtqJVhnK16K5v1Gg0YKJ4fbffodwQstLhinQwjNEb+rfXusmzKyjV6nu
+zZ1C6+b5wijQenybIxRIm4sWFGxm5k5IFQiPQXN2bzkAHVfBAkEA4Bm1ohumDcRd
+2G/KuUdaiNdM6/GkviyXPDKa4Jvkh3pLcVzd2xa9Eb5Hc7tiwWdL8LPEbqssxHpu
+Eeics5NMCwJBANuqNF3aaufOKHtwsDg4c1G/mh3MweoNXAFNVCfW+a9E/0nKVqiV
+194XfRaq24w8iN5vG4l8wPbKUHiVitZSBPkCQQDZ4IUT/7eZKPJorH+3VSUzZ8e0
+mXInJyk+cKDdgRNVmFPi+nwENv5JUusbHPMtv+U4Nz7ire/PcoyzQqmfebTzAkAi
+XMHMXLqUgH98r+ghI1OG2j41oy5CesyFt58OjGaKsHRvCqP7w1T9fRcoUJn/mgif
+Iypfm5BkCsoD0wDg/fuhAkEA3APsWcyaOdTIRdmjnJVZ5wlWKRgMnPT6kTwkRlY8
+Fl1c5uNZnxtpgmOd+10O5a/16aNoumsFfDP4z/anAeo0NA==
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match.pem
new file mode 100644
index 0000000..894542a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQDuRPOTLKY/nLoqg7C9pXSlyNOooERN66v1TXJa2G/OdLtK0Yqo
+muXipwyVx6B9yIRvY7LDCfTqDAb3meIOw/DPRDAzCPhpeXpjNG3tqc/3m8rdJCXN
+vA7MF8wfjBwVe1/KfiYV3KVUfpxHRlkegPA35MPXlt9I0qcU2I96eO7zXQIDAQAB
+AoGAOl3dpPHND8wetodn5iz/tBTL9IS99rCCgSalmqdmTBbAQoWuB7taEPmG1bH/
+GJDkED1F7w0jV6n2kvS5MZDCDirPulZgQEhDSIIt8H7epLgZ9duv2axi3C4F3SOZ
+BJihh7EQVwXA1BMoyV6a3mhkOpJWoAt9O3F+pU7tgUymSakCQQD3o9wLjeBPmZST
+4kszwcASBlvXqLUGvdD6eg9n/gfVx0wkQPeKMVzMB78W2hDysGvK7e5IYiLXOUvl
+d50NRzL/AkEA9lAb0/Ja2QGMXQWtCVkjf3Sdh3ZYdROcaXqUaETU0fzitPqk+FwE
+5rXiCTi+/BOMFXqpQ9ECqmu7JaEwl+WFowJBANXHny5aTEprSthfgowrtqPY2XBT
+M/Od6cpRlPsxUZI7en/RleC2vGVmSpBvsDHSGzwUKqBSF8G0tNBjcjEERgECQQDH
+LdiS97RHL11WC7T1jkOKWb7ZP/YgFo+xLBK+joqalCivuM+WlrLP7dyvIFgwd44c
+AGjIEkhZj2xy7XniGxSJAkBfZ6/FU6TzTtXEppqN5Y19FswaLpG4JExZddxfmN57
+dhN1j5jjXlldpZXpkKXM/DwxPfNI6vVq+p771sTdFY9l
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_ip.pem
new file mode 100644
index 0000000..1aab227
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_ip.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDCBO/5Uammcq0j5699UYvcftBxM12TZVJued8wWM8ITkeNSh9q
+6khnaCcQDajCgOgNEBI0MunYd+UV7JyVuvQWV+Wqbxie7k5s70zzv5HwS7CNwP+P
+bvHzuTGOc9lcxV/9MK42uoCqda5NUDu6s5qm0jpFIMN+hnmvdNfC1MHU+wIDAQAB
+AoGBAKhLwB10SMSpFKbggckiwz8wgX6wH6uLaEWMUZ2Oa4e43zgf48DKGKQaJEg4
+kPqm9qLeHexJfa7X0U+DPxBiavazw/oEoHKf3pLOm/HQfr0gKik4hYxeBk1tqLhR
+7xUbzcxAWw9cBKr3qzrvKp86S1LzUD0nkLvH4pouCSgTcf6BAkEA5wVKh6Iaf8zs
++hyIvPVldOQPMyfW6XRUFkJj4wueGIlyAgzqKYGjVmgQ4ZI7JnooY9zT9U03vecL
+LocRQeAc9wJBANb/cF2COqYyTYjBvps29cTTOZajdhLYJB8yJcMici3epjW682gN
+epDM0SwiHsN6f7vzTsQTxEJGi7O/MY2Tmx0CQBkNbtakkqEXVP+OhpmPNxBlBuzy
+PhEVB2Vej7x4bw2UIiPsyJv1rgPZxzLfC8ERcD3cHbQ1Sn0yh/jkts9hAWMCQQCz
+LxkbFdqDgREVlUOiYN/cNp6caYeSlQkl5smKJD88839a8IDEi9dGryz1t0okaQfk
+QRe4WzEKe3kbSZGnCQoRAkEAmQBdntXZgFCrdMQbYvGGmjnPHGYK/OeKZN2ZVIKI
+VKMTz74yAiA6l/W0D7KY+aR+bm3lxUPCqIdryA7K1JhGJA==
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_wc.pem
new file mode 100644
index 0000000..e296fac
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_match_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQCuOiaT7sve9lu1+6MutivM60kadjhgMOt/mpn10n/9yNq98zhL
+fSf7FYEjWSKSH613919Q52niZCpoM2NiAhD6LOEGrswEeeWMH+qaJp4DsE+0+Bwr
+IWuwbWgkrgwupzZ7J9TxE+91XuIRSfzU+E0tYz7ICESjyHlCToWlXquRUwIDAQAB
+AoGAJWbcY0OPRpUSiRW1KVD4RCx6bBDW92dpFuKkjI4c5elZdA+jlSkQDSnHqEP5
+VO8x+SAEuUSEiZJBPE9T1XVmGUQczf87ZQHZRSwrIohih/MrvyrNakszD1VPlGIs
+fLwKAMFfM2/aXignZx9UJaMcTvzDNZ/FhlxgzYnrukfzPUECQQDVe/BijfLQs711
+KZyYWMchW84LcXYa3mtt2EVwfmZlvAlKIe0WyJ9KskYvssWKLIHcZvRIHiQgs+T/
+pPBUmgkhAkEA0OzF3uU2oX4aVhmu8H3R9AmH7mKFjySdTAFPyZMdh/AonsKO+B1t
+wGI+9jOgDC3qOp3GVygUTO7xpa+qSpUH8wJARrxZSx6DjRlxNigChgwsduYnYG1I
+1+BIsk3NvFd1cFIctd6F4124QhTN4rIWeBEFOlU2rcqm59sTjo1FQJMFYQJBAJYb
+uGNYjrLjNqrsExwpWmMbQfYOBWX+aaHFQ31R3SLhc1317eDozAUw0Yn1N6Xoi2UI
+HQxb+JH6D9b6asDhaO8CQCqNag8fEhmXzWiZDM+ngreFJsFFYiwwbcSOA0p3JBG/
+o39PeTTW4VC8+Y7KIO5rM8ZzuT3cArfsZLC8erAc2oI=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch.pem
new file mode 100644
index 0000000..c057fa6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQC9mE1ukT8FZc/uqiQWs0S62Imxyb0QCnCSmd7qYwTs1ePVPUEg
+96sAzCGjub+Igz0U9KmElSNsmNXTkB/LwGyQcVf9OebkmSmD1ihY08o/cAak8CWC
+sXNOA6bZUc1rcMapTHU2Tus3RnwcLs+bvuWiDhshdOhyoq3tsTygStTDFwIDAQAB
+AoGAVhUcsyMHdi/v5PEjSZtDjYJbonlyeiIDUszCRbGHvvCubVdOhuVsowQMEgZ4
+TEcqKKWdgjEk0F3kWBaMGTrZD0OlvjkhRDvR53P6mvDuN1+rDo4NxYe3GPX+Rylw
+YmkqJNkbspwPeu2tjD7gCRio/PiaQVAHmnZEi7JY0pjPH8ECQQDrh+3paJyZ3USt
+d+znBZwvXOH5tdEd7TMeSRlaETJIR+H+9043v4jbk2myH6fq/Ga2qKNEHJwj3A/q
+yYWHbdvPAkEAzhJkdmL61V03/Bs3EfV3q1K+7T7C+gBEJeXGgjfNbnPaKubej2R4
+yrYX3WLVKNwAyn9QBdc1C/3Li7On1+6OOQJAJnf9E7JSYY8SOILt3o28BMBW6EIZ
+9ZlYUAoAagWFAOk8sDgUPma6I6LgEIe7FVZEMHE6uVUy62h18RMZDBWVYQJBAMOD
+WcOkxz1WEz7jB03HqgFnIXRwPob5Z67ZIYz8qrDnxA8+OdysQ5p4R44pLfp1OV7m
+iaLw+whkhBEIo+9QC6kCQQDqJiutzJMhsp4hIDhB/hhmWlkbe5LZOFqLLejvSvhF
++vQb9bJ7OjcOD+xl6Zq0QZWg6PVtw+mV8rBqnvhn6Rgi
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_ip.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_ip.pem
new file mode 100644
index 0000000..1e1cf9f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_ip.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXAIBAAKBgQDGZb/yn/1nlvn4aWuDyMbZqFMDDrV8efmDBW9g2AzsM7cqlUjS
+69C6zd4McezG76vqTtBNRuHQTZ1MMUBpCQLRZgzCvmvl6vUVOBayNCCNGe5hsEzT
+WezDZP02VONJL+6L4wZC7tevXTFsQ8a0QUDc4rPt9pXxnOxy2ZwHrzLMQQIDAQAB
+AoGAYlsLH9NlIzfb0OUiYgDlY/5ouPlPy9gEdIn7P32cWnUA92Fu6vHxVsq8rFrG
+UZF8HvrXlZfwVv5DXwSDtA69kAXKWgcStRK6idBLZZjpQqg/a05ajTuSaxeBh/PV
+63Ddff5W+GDnaFIsDxjSEscafYNFoYD6uaq9EuzbOjpSsOECQQD1kmH9s4Io9QFB
+wQ9WEpzc1GdoOq6RVTyWkGoA1Vvzb+3DphjlLK1OpFLzIEXFdOuqWdCul+MTsqEu
+U/k+V651AkEAztKIlF6YcY4jajrtEppaD+uyqPjiwO+YwY+dBpXuELmwtBEtDHV/
+Y5kMu5BLNNND5GFzqmNNIkCxRQ25lXTFHQJAKDPLuQXvNBdQGlypzxSC+6AGQckB
+lSdYIOoJgJNtV0Ams5X4k2QqByrdrZHF+lp2zk+f20VgVnQy8OusbSlblQJAeJFZ
+F41RtxkSYjI51BdxG0je3QxCPBEw7t3Gv3kV0GA/t6kvpsy0V554QrtqbEAgmvmI
+kTLUW3x1GvlWT99DrQJBAJH+dTB8xvxHxTC/KxErx48imZGZ/lqlpAeADY7ZMv2i
+Aidlu36/giQLmVPPpGdHbXmrXsR7PRn/woak5PZG8TI=
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_wc.pem b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_wc.pem
new file mode 100644
index 0000000..6a969e6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/est/test/san/key_san_mismatch_wc.pem
@@ -0,0 +1,15 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQCgQmtzWAzyhW6vULMZRKFEAxUr4vULNCCjyPLVA5xfgPftKfWe
+1A5fZJmi8d2O0VuZhUgab30dUNMTe0jfLGBiS1qLGS8Aw7MJHAlRJ+ur7fsGYjFp
++x75ERJ13aT5ipnp9EiW24m4ZPxVf19PG4kHDAW4qk3G5kHuesP0JZNlUwIDAQAB
+AoGACOzqExh6UB+CVJ1H/EZIPQN28GTJhQA4OSb970MuOFt0c2780QW6bzGpJNwX
+nNaoJVq480/2ReW0e8dH/bX5ACnf/sZnLdhhOQ450ltB7KD+/B8U0nHwL5t0m8yj
+cq3CbYDa+xHc9DJOBc0bGzCPwHcMVPrutwk/uSbxwdKSk4ECQQDUTRIrWX+SlRyL
+84ImjwH8eS3Ak/WxyTy0kA88tzDrS6zjUKtYnVbdRhKP793p1qCAbRqG3mO8npGw
+2kVSfs+zAkEAwT8VcmVoiNp97OKRWW8wHvuqef3bDdCB5Gtyvrg/NZoJxrrECQYM
+A9bMLzJDETpBCTnKYFXcFNMHCNUCQ7FD4QJBAMsghr7lXfNxKiQZtDCSafMYOpdk
+uhMGGUgmAf40xOCMwnmoaJlbyY3jisUfWzNugJkASv458DG7gmg1H1jELpMCQEW9
+4puahZN+Zxvq2NISEqfb47aLK1dc/MPXE15JWGab5RNXrBAloohwKNiPpyy8fWQ6
++xKxafAKCOvjFCIiA4ECQQC0zNtjb+4T/zG25PzfRB8qTppRWi0gH+8TXExXuW92
+8icrxcynK0GXTZehoC5LnTeijixA/3Z2FPhYQmnq/GJi
+-----END RSA PRIVATE KEY-----
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/BasicMimeParser.java b/bcpkix/src/main/java/org/bouncycastle/mime/BasicMimeParser.java
new file mode 100644
index 0000000..992acb2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/BasicMimeParser.java
@@ -0,0 +1,137 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.mime.encoding.Base64InputStream;
+import org.bouncycastle.mime.encoding.QuotedPrintableInputStream;
+
+public class BasicMimeParser
+    implements MimeParser
+{
+    private final InputStream src;
+    private final MimeParserContext parserContext;
+    private final String defaultContentTransferEncoding;
+    private Headers headers;
+
+    private boolean isMultipart = false;
+    private final String boundary;
+
+    public BasicMimeParser(
+        InputStream src)
+        throws IOException
+    {
+        this(null, new Headers(src, "7bit"), src);
+    }
+
+    public BasicMimeParser(
+        MimeParserContext parserContext, InputStream src)
+        throws IOException
+    {
+        this(parserContext, new Headers(src, parserContext.getDefaultContentTransferEncoding()), src);
+    }
+
+    public BasicMimeParser(
+        Headers headers, InputStream content)
+    {
+        this(null, headers, content);
+    }
+
+    public BasicMimeParser(
+        MimeParserContext parserContext, Headers headers, InputStream src)
+    {
+        if (headers.isMultipart())
+        {
+            isMultipart = true;
+            boundary = headers.getBoundary();
+        }
+        else
+        {
+            boundary = null;
+        }
+
+        this.headers = headers;
+        this.parserContext = parserContext;
+        this.src = src;
+        this.defaultContentTransferEncoding = (parserContext != null) ? parserContext.getDefaultContentTransferEncoding() : "7bit";
+    }
+
+
+
+    public void parse(MimeParserListener listener)
+        throws IOException
+    {
+        MimeContext context = listener.createContext(parserContext, headers);
+
+        String s;
+        if (isMultipart)    // Signed
+        {
+            MimeMultipartContext mContext = (MimeMultipartContext)context;
+            String startBoundary = "--" + boundary;
+            boolean startFound = false;
+            int partNo = 0;
+            LineReader rd = new LineReader(src);
+            while ((s = rd.readLine()) != null && !"--".equals(s))
+            {
+                if (startFound)
+                {
+                    InputStream inputStream = new BoundaryLimitedInputStream(src, boundary);
+                    Headers headers = new Headers(inputStream, defaultContentTransferEncoding);
+
+                    MimeContext partContext = mContext.createContext(partNo++);
+                    inputStream = partContext.applyContext(headers, inputStream);
+
+                    listener.object(parserContext, headers, processStream(headers, inputStream));
+
+                    if (inputStream.read() >= 0)
+                    {
+                        throw new IOException("MIME object not fully processed");
+                    }
+                }
+                else if (startBoundary.equals(s))
+                {
+                    startFound = true;
+                    InputStream inputStream = new BoundaryLimitedInputStream(src, boundary);
+                    Headers headers = new Headers(inputStream, defaultContentTransferEncoding);
+
+                    MimeContext partContext = mContext.createContext(partNo++);
+                    inputStream = partContext.applyContext(headers, inputStream);
+
+                    listener.object(parserContext, headers, processStream(headers, inputStream));
+
+                    if (inputStream.read() >= 0)
+                    {
+                        throw new IOException("MIME object not fully processed");
+                    }
+                }
+            }
+        }
+        else
+        {
+            InputStream inputStream = context.applyContext(headers, src);
+
+            listener.object(parserContext, headers, processStream(headers, inputStream));
+        }
+    }
+
+    public boolean isMultipart()
+    {
+        return isMultipart;
+    }
+
+    private InputStream processStream(Headers headers, InputStream inputStream)
+    {
+        if (headers.getContentTransferEncoding().equals("base64"))
+        {
+            return new Base64InputStream(inputStream);
+        }
+        else if (headers.getContentTransferEncoding().equals("quoted-printable"))
+        {
+            return new QuotedPrintableInputStream(inputStream);
+        }
+        else
+        {
+            return inputStream;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/BoundaryLimitedInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/BoundaryLimitedInputStream.java
new file mode 100644
index 0000000..585e696
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/BoundaryLimitedInputStream.java
@@ -0,0 +1,121 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Strings;
+
+public class BoundaryLimitedInputStream
+    extends InputStream
+{
+    final private InputStream src;
+    final private byte[] boundary;
+    final private byte[] buf;
+
+    private int bufOff = 0;
+    private int index = 0;
+    private boolean ended = false;
+
+    private int lastI;
+
+    public BoundaryLimitedInputStream(InputStream src, String startBoundary)
+    {
+        this.src = src;
+        boundary = Strings.toByteArray(startBoundary);
+        this.buf = new byte[startBoundary.length() + 3];
+        this.bufOff = 0;
+    }
+
+
+    public int read()
+        throws IOException
+    {
+        if (ended)
+        {
+            return -1;
+        }
+
+        int i;
+        if (index < bufOff)
+        {
+            i = buf[index++] & 0xff;
+            // if this happens we know we've already failed on a "\r\n"
+            if (index < bufOff)
+            {
+                return i;
+            }
+            index = bufOff = 0;
+        }
+        else
+        {
+            i = src.read();
+        }
+
+        lastI = i;
+
+        if (i < 0)
+        {
+            return -1;
+        }
+
+        if (i == '\r' || i == '\n') // check for start of boundary
+        {
+            int ch;
+            index = 0;
+            if (i == '\r')
+            {
+                ch = src.read();
+                if (ch == '\n')
+                {
+                    buf[bufOff++] = '\n';
+                    ch = src.read();
+                }
+            }
+            else
+            {
+                ch = src.read();
+            }
+
+            if (ch == '-')
+            {
+                buf[bufOff++] = '-';
+                ch = src.read();
+            }
+
+            if (ch == '-')
+            {
+                buf[bufOff++] = '-';
+
+                int base = bufOff;
+                int c;
+
+                while ((bufOff - base) != boundary.length && (c = src.read()) >= 0)
+                {
+                    buf[bufOff] = (byte)c;
+                    if (buf[bufOff] != boundary[bufOff - base])
+                    {
+                        bufOff++;
+                        break;
+                    }
+                    bufOff++;
+                }
+                
+                // we have a match
+                if (bufOff - base == boundary.length)
+                {
+                    ended = true;
+                    return -1;
+                }
+            }
+            else
+            {
+                if (ch >= 0)
+                {
+                    buf[bufOff++] = (byte)ch;
+                }
+            }
+        }
+
+        return i;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/CanonicalOutputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/CanonicalOutputStream.java
new file mode 100644
index 0000000..41d7d54
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/CanonicalOutputStream.java
@@ -0,0 +1,87 @@
+package org.bouncycastle.mime;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.mime.smime.SMimeParserContext;
+
+public class CanonicalOutputStream
+    extends FilterOutputStream
+{
+    protected int lastb;
+    protected static byte newline[];
+    private final boolean is7Bit;
+
+    public CanonicalOutputStream(SMimeParserContext parserContext, Headers headers, OutputStream outputstream)
+    {
+        super(outputstream);
+        lastb = -1;
+        // TODO: eventually may need to handle multiparts with binary...
+        if (headers.getContentType() != null)
+        {
+            is7Bit = headers.getContentType() != null && !headers.getContentType().equals("binary");
+        }
+        else
+        {
+            is7Bit = parserContext.getDefaultContentTransferEncoding().equals("7bit");
+        }
+    }
+
+    public void write(int i)
+        throws IOException
+    {
+        if (is7Bit)
+        {
+            if (i == '\r')
+            {
+                out.write(newline);
+            }
+            else if (i == '\n')
+            {
+                if (lastb != '\r')
+                {
+                    out.write(newline);
+                }
+            }
+            else
+            {
+                out.write(i);
+            }
+        }
+        else
+        {
+            out.write(i);
+        }
+        
+        lastb = i;
+    }
+
+    public void write(byte[] buf)
+        throws IOException
+    {
+        this.write(buf, 0, buf.length);
+    }
+
+    public void write(byte buf[], int off, int len)
+        throws IOException
+    {
+        for (int i = off; i != off + len; i++)
+        {
+            this.write(buf[i]);
+        }
+    }
+
+    public void writeln()
+        throws IOException
+    {
+        super.out.write(newline);
+    }
+
+    static 
+    {
+        newline = new byte[2];
+        newline[0] = '\r';
+        newline[1] = '\n';
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/ConstantMimeContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/ConstantMimeContext.java
new file mode 100644
index 0000000..5afd06c
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/ConstantMimeContext.java
@@ -0,0 +1,20 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class ConstantMimeContext
+    implements MimeContext, MimeMultipartContext
+{
+    public InputStream applyContext(Headers headers, InputStream contentStream)
+        throws IOException
+    {
+        return contentStream;
+    }
+
+    public MimeContext createContext(int partNo)
+        throws IOException
+    {
+        return this;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/Headers.java b/bcpkix/src/main/java/org/bouncycastle/mime/Headers.java
new file mode 100644
index 0000000..3b67089
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/Headers.java
@@ -0,0 +1,254 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.bouncycastle.util.Iterable;
+import org.bouncycastle.util.Strings;
+
+public class Headers
+    implements Iterable<String>
+{
+    private final Map<String, List> headers = new TreeMap<String, List>(String.CASE_INSENSITIVE_ORDER);
+    private final List<String> headersAsPresented;
+    private final String contentTransferEncoding;
+
+    private String boundary;
+    private boolean multipart;
+    private String contentType;
+    private Map<String, String> contentTypeParameters;
+
+    private static List<String> parseHeaders(InputStream src)
+        throws IOException
+    {
+        String s;
+        List<String> headerLines = new ArrayList<String>();
+        LineReader   rd = new LineReader(src);
+
+        while ((s = rd.readLine()) != null)
+        {
+            if (s.length() == 0)
+            {
+                break;
+            }
+            headerLines.add(s);
+        }
+
+        return headerLines;
+    }
+
+    public Headers(InputStream source, String defaultContentTransferEncoding)
+        throws IOException
+    {
+        this(parseHeaders(source), defaultContentTransferEncoding);
+    }
+
+    public Headers(List<String> headerLines, String defaultContentTransferEncoding)
+    {
+        this.headersAsPresented = headerLines;
+
+        String header = "";
+        for (Iterator it = headerLines.iterator(); it.hasNext();)
+        {
+            String line = (String)it.next();
+            if (line.startsWith(" ") || line.startsWith("\t"))
+            {
+                header = header + line.trim();
+            }
+            else
+            {
+                if (header.length() != 0)
+                {
+                    this.put(header.substring(0, header.indexOf(':')).trim(), header.substring(header.indexOf(':') + 1).trim());
+                }
+                header = line;
+            }
+        }
+
+        // pick up last header line
+        if (header.trim().length() != 0)
+        {
+            this.put(header.substring(0, header.indexOf(':')).trim(), header.substring(header.indexOf(':') + 1).trim());
+        }
+
+        String contentTypeHeader = (this.getValues("Content-Type") == null) ? "text/plain" : this.getValues("Content-Type")[0];
+
+        int parameterIndex = contentTypeHeader.indexOf(';');
+        if (parameterIndex < 0)
+        {
+            contentType = contentTypeHeader;
+            contentTypeParameters = Collections.EMPTY_MAP;
+        }
+        else
+        {
+            contentType = contentTypeHeader.substring(0, parameterIndex);
+            contentTypeParameters = createContentTypeParameters(contentTypeHeader.substring(parameterIndex + 1).trim());
+        }
+
+        contentTransferEncoding = this.getValues("Content-Transfer-Encoding") == null ? defaultContentTransferEncoding : this.getValues("Content-Transfer-Encoding")[0];
+
+        if (contentType.indexOf("multipart") >= 0)
+        {
+            multipart = true;
+            String bound = (String)contentTypeParameters.get("boundary");
+            boundary = bound.substring(1, bound.length() - 1); // quoted-string
+        }
+        else
+        {
+            boundary = null;
+            multipart = false;
+        }
+    }
+
+    /**
+     * Return the a Map of the ContentType attributes and their values.
+     *
+     * @return a Map of ContentType parameters - empty if none present.
+     */
+    public Map<String, String> getContentTypeAttributes()
+    {
+        return contentTypeParameters;
+    }
+
+    /**
+     * Return the a list of the ContentType parameters.
+     *
+     * @return a list of ContentType parameters - empty if none present.
+     */
+    private Map<String, String> createContentTypeParameters(String contentTypeParameters)
+    {
+        String[] parameterSplit = contentTypeParameters.split(";");
+        Map<String, String> rv = new LinkedHashMap<String, String>();
+
+        for (int i = 0; i != parameterSplit.length; i++)
+        {
+            String parameter = parameterSplit[i];
+
+            int eqIndex = parameter.indexOf('=');
+            if (eqIndex < 0)
+            {
+                throw new IllegalArgumentException("malformed Content-Type header");
+            }
+
+            rv.put(parameter.substring(0, eqIndex).trim(), parameter.substring(eqIndex + 1).trim());
+        }
+
+        return Collections.unmodifiableMap(rv);
+    }
+
+    public boolean isMultipart()
+    {
+        return multipart;
+    }
+
+    public String getBoundary()
+    {
+        return boundary;
+    }
+
+    public String getContentType()
+    {
+        return contentType;
+    }
+
+    public String getContentTransferEncoding()
+    {
+        return contentTransferEncoding;
+    }
+
+    private void put(String field, String value)
+    {
+        synchronized (this)
+        {
+            KV kv = new KV(field, value);
+            List<KV> list = (List<KV>)headers.get(field);
+            if (list == null)
+            {
+                list = new ArrayList<KV>();
+                headers.put(field, list);
+            }
+            list.add(kv);
+        }
+    }
+
+    public Iterator<String> getNames()
+    {
+        return headers.keySet().iterator();
+    }
+
+    public String[] getValues(String header)
+    {
+
+        synchronized (this)
+        {
+            List<KV> kvList = (List<KV>)headers.get(header);
+            if (kvList == null)
+            {
+                return null;
+            }
+            String[] out = new String[kvList.size()];
+
+            for (int t = 0; t < kvList.size(); t++)
+            {
+                out[t] = ((KV)kvList.get(t)).value;
+            }
+
+            return out;
+        }
+    }
+
+    public boolean isEmpty()
+    {
+        synchronized (this)
+        {
+            return headers.isEmpty();
+        }
+    }
+
+    public boolean containsKey(String s)
+    {
+        return headers.containsKey(s);
+    }
+
+    public Iterator<String> iterator()
+    {
+        return headers.keySet().iterator();
+    }
+
+    public void dumpHeaders(OutputStream outputStream)
+        throws IOException
+    {
+        for (Iterator it = headersAsPresented.iterator(); it.hasNext();)
+        {
+            outputStream.write(Strings.toUTF8ByteArray(it.next().toString()));
+            outputStream.write('\r');
+            outputStream.write('\n');
+        }
+    }
+
+    private class KV
+    {
+        public final String key;
+        public final String value;
+
+        public KV(String key, String value)
+        {
+            this.key = key;
+            this.value = value;
+        }
+
+        public KV(KV kv)
+        {
+            this.key = kv.key;
+            this.value = kv.value;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/LineReader.java b/bcpkix/src/main/java/org/bouncycastle/mime/LineReader.java
new file mode 100644
index 0000000..f0e4aea
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/LineReader.java
@@ -0,0 +1,66 @@
+package org.bouncycastle.mime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.util.Strings;
+
+/**
+ * Read regular text lines, allowing for a single character look ahead.
+ */
+class LineReader
+{
+    private final InputStream src;
+
+    private int lastC = -1;
+
+    LineReader(InputStream src)
+    {
+        this.src = src;
+    }
+
+    String readLine()
+        throws IOException
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        int ch;
+
+        if (lastC != -1)
+        {
+            if (lastC == '\r')   // to get this we must have '\r\r' so blank line
+            {
+                return "";
+            }
+            ch = lastC;
+            lastC = -1;
+        }
+        else
+        {
+            ch = src.read();
+        }
+
+        while (ch >= 0 && ch != '\r' && ch != '\n')
+        {
+            bOut.write(ch);
+            ch = src.read();
+        }
+
+        if (ch == '\r')
+        {
+            int c = src.read();
+            if (c != '\n' && c >= 0)
+            {
+                lastC = c;
+            }
+        }
+
+        if (ch < 0)
+        {
+            return null;
+        }
+
+        return Strings.fromUTF8ByteArray(bOut.toByteArray());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeContext.java
new file mode 100644
index 0000000..5b7b31e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeContext.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface MimeContext
+{
+    InputStream applyContext(Headers headers, InputStream contentStream)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeIOException.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeIOException.java
new file mode 100644
index 0000000..67e6c3e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeIOException.java
@@ -0,0 +1,29 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+
+/**
+ * General IOException thrown in the mime package and its sub-packages.
+ */
+public class MimeIOException
+    extends IOException
+{
+    private Throwable cause;
+
+    public MimeIOException(String msg, Throwable cause)
+    {
+        super(msg);
+
+        this.cause = cause;
+    }
+
+    public MimeIOException(String msg)
+    {
+        super(msg);
+    }
+
+    public Throwable getCause()
+    {
+        return cause;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeMultipartContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeMultipartContext.java
new file mode 100644
index 0000000..223ab06
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeMultipartContext.java
@@ -0,0 +1,10 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+
+public interface MimeMultipartContext
+    extends MimeContext
+{
+    public MimeContext createContext(int partNo)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParser.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParser.java
new file mode 100644
index 0000000..edd9cc3
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParser.java
@@ -0,0 +1,18 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+
+/**
+ * Base interface for a MIME parser.
+ */
+public interface MimeParser
+{
+    /**
+     * Trigger the start of parsing.
+     *
+     * @param listener callback to be signalled as each MIME object is identified.
+     * @throws IOException on a parsing/IO exception.
+     */
+    void parse(MimeParserListener listener)
+        throws IOException;
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserContext.java
new file mode 100644
index 0000000..f00476f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserContext.java
@@ -0,0 +1,14 @@
+package org.bouncycastle.mime;
+
+/**
+ * Base interface for a MIME parser context.
+ */
+public interface MimeParserContext
+{
+    /**
+     * Return the default value for Content-Transfer-Encoding for data we are parsing.
+     *
+     * @return the default Content-Transfer-Encoding.
+     */
+    String getDefaultContentTransferEncoding();
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserListener.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserListener.java
new file mode 100644
index 0000000..d22e870
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserListener.java
@@ -0,0 +1,30 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Base interface for a MIME parser listener.
+ */
+public interface MimeParserListener
+{
+    /**
+     * Create an appropriate context object for the MIME object represented by headers.
+     *
+     * @param parserContext context object for the current parser.
+     * @param headers MIME headers for the object that has been discovered.
+     * @return a MimeContext
+     */
+    MimeContext createContext(MimeParserContext parserContext, Headers headers);
+
+    /**
+     * Signal that a MIME object has been discovered.
+     *
+     * @param parserContext context object for the current parser.
+     * @param headers headers for the MIME object.
+     * @param inputStream input stream representing its content.
+     * @throws IOException in case of a parsing/processing error.
+     */
+    void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserProvider.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserProvider.java
new file mode 100644
index 0000000..d81e1f7
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeParserProvider.java
@@ -0,0 +1,13 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public interface MimeParserProvider
+{
+    MimeParser createParser(InputStream source)
+        throws IOException;
+
+    MimeParser createParser(Headers headers, InputStream source)
+        throws IOException;
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/MimeWriter.java b/bcpkix/src/main/java/org/bouncycastle/mime/MimeWriter.java
new file mode 100644
index 0000000..cf08d63
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/MimeWriter.java
@@ -0,0 +1,41 @@
+package org.bouncycastle.mime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+public abstract class MimeWriter
+{
+    protected final Headers headers;
+
+    protected MimeWriter(Headers headers)
+    {
+        this.headers = headers;
+    }
+
+    public Headers getHeaders()
+    {
+        return headers;
+    }
+
+    public abstract OutputStream getContentStream()
+        throws IOException;
+
+
+    protected static List<String> mapToLines(Map<String, String> headers)
+    {
+        List hdrs = new ArrayList(headers.size());
+
+        for (Iterator<String> it = headers.keySet().iterator(); it.hasNext();)
+        {
+            String key = (String)it.next();
+
+            hdrs.add(key + ": " + headers.get(key));
+        }
+
+        return hdrs;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64InputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64InputStream.java
new file mode 100644
index 0000000..75334ac
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64InputStream.java
@@ -0,0 +1,182 @@
+package org.bouncycastle.mime.encoding;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Reader for Base64 armored objects which converts them into binary data.
+ */
+public class Base64InputStream
+    extends InputStream
+{
+    /*
+     * set up the decoding table.
+     */
+    private static final byte[] decodingTable;
+
+    static
+    {
+        decodingTable = new byte[128];
+
+        for (int i = 'A'; i <= 'Z'; i++)
+        {
+            decodingTable[i] = (byte)(i - 'A');
+        }
+
+        for (int i = 'a'; i <= 'z'; i++)
+        {
+            decodingTable[i] = (byte)(i - 'a' + 26);
+        }
+
+        for (int i = '0'; i <= '9'; i++)
+        {
+            decodingTable[i] = (byte)(i - '0' + 52);
+        }
+
+        decodingTable['+'] = 62;
+        decodingTable['/'] = 63;
+    }
+
+    /**
+     * decode the base 64 encoded input data.
+     *
+     * @return the offset the data starts in out.
+     */
+    private int decode(
+        int      in0,
+        int      in1,
+        int      in2,
+        int      in3,
+        int[]    out)
+        throws EOFException
+    {
+        int    b1, b2, b3, b4;
+
+        if (in3 < 0)
+        {
+            throw new EOFException("unexpected end of file in armored stream.");
+        }
+
+        if (in2 == '=')
+        {
+            b1 = decodingTable[in0] &0xff;
+            b2 = decodingTable[in1] & 0xff;
+
+            out[2] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+
+            return 2;
+        }
+        else if (in3 == '=')
+        {
+            b1 = decodingTable[in0];
+            b2 = decodingTable[in1];
+            b3 = decodingTable[in2];
+
+            out[1] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+            out[2] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+
+            return 1;
+        }
+        else
+        {
+            b1 = decodingTable[in0];
+            b2 = decodingTable[in1];
+            b3 = decodingTable[in2];
+            b4 = decodingTable[in3];
+
+            out[0] = ((b1 << 2) | (b2 >> 4)) & 0xff;
+            out[1] = ((b2 << 4) | (b3 >> 2)) & 0xff;
+            out[2] = ((b3 << 6) | b4) & 0xff;
+
+            return 0;
+        }
+    }
+
+    InputStream    in;
+    int[]          outBuf = new int[3];
+    int            bufPtr = 3;
+    boolean        isEndOfStream;
+
+    /**
+     * Create a stream for reading a PGP armoured message, parsing up to a header
+     * and then reading the data that follows.
+     *
+     * @param in
+     */
+    public Base64InputStream(
+        InputStream    in)
+    {
+        this.in = in;
+    }
+    
+    public int available()
+        throws IOException
+    {
+        return in.available();
+    }
+    
+    private int readIgnoreSpace() 
+        throws IOException
+    {
+        int    c = in.read();
+        
+        while (c == ' ' || c == '\t')
+        {
+            c = in.read();
+        }
+        
+        return c;
+    }
+    
+    public int read()
+        throws IOException
+    {
+        int    c;
+
+        if (bufPtr > 2)
+        {
+            c = readIgnoreSpace();
+            
+            if (c == '\r' || c == '\n')
+            {
+                c = readIgnoreSpace();
+                
+                while (c == '\n' || c == '\r')
+                {
+                    c = readIgnoreSpace();
+                }
+
+                if (c < 0)                // EOF
+                {
+                    isEndOfStream = true;
+                    return -1;
+                }
+
+                bufPtr = decode(c, readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf);
+            }
+            else
+            {
+                if (c >= 0)
+                {
+                    bufPtr = decode(c, readIgnoreSpace(), readIgnoreSpace(), readIgnoreSpace(), outBuf);
+                }
+                else
+                {
+                    isEndOfStream = true;
+                    return -1;
+                }
+            }
+        }
+
+        c = outBuf[bufPtr++];
+
+        return c;
+    }
+    
+    public void close()
+        throws IOException
+    {
+        in.close();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64OutputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64OutputStream.java
new file mode 100644
index 0000000..51e7b1f
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/Base64OutputStream.java
@@ -0,0 +1,63 @@
+package org.bouncycastle.mime.encoding;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import org.bouncycastle.util.encoders.Base64;
+
+public class Base64OutputStream
+    extends FilterOutputStream
+{
+    byte[] buffer = new byte[54];
+    int    bufOff;
+
+    public Base64OutputStream(OutputStream stream)
+    {
+        super(stream);
+    }
+
+    public void write(int b)
+        throws IOException
+    {
+        doWrite((byte)b);
+    }
+
+    public void write(byte[] buf, int bufOff, int len)
+        throws IOException
+    {
+        for (int i = 0; i != len; i++)
+        {
+            doWrite(buf[bufOff + i]);
+        }
+    }
+
+    public void write(byte[] buf)
+        throws IOException
+    {
+        write(buf, 0, buf.length);
+    }
+
+    public void close()
+        throws IOException
+    {
+        if (bufOff > 0)
+        {
+            Base64.encode(buffer, 0, bufOff, out);
+        }
+        out.close();
+    }
+    
+    private void doWrite(byte b)
+        throws IOException
+    {
+        buffer[bufOff++] = b;
+        if (bufOff == buffer.length)
+        {
+            Base64.encode(buffer, 0, buffer.length, out);
+            out.write('\r');
+            out.write('\n');
+            bufOff = 0;
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/encoding/QuotedPrintableInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/QuotedPrintableInputStream.java
new file mode 100644
index 0000000..4f82309
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/encoding/QuotedPrintableInputStream.java
@@ -0,0 +1,124 @@
+package org.bouncycastle.mime.encoding;
+
+import java.io.FilterInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Input stream that processes quoted-printable data, converting it into what was originally intended.
+ */
+public class QuotedPrintableInputStream
+    extends FilterInputStream
+{
+
+    public QuotedPrintableInputStream(InputStream input)
+    {
+        super(input);
+    }
+
+    public int read(byte[] buf, int bufOff, int len) throws IOException
+    {
+        int i = 0;
+        while (i != len)
+        {
+            int ch = this.read();
+            if (ch < 0)
+            {
+                break;
+            }
+            buf[i + bufOff] = (byte)ch;
+            i++;
+        }
+
+        if (i == 0)
+        {
+            return -1;
+        }
+        
+        return i;
+    }
+
+    public int read()
+        throws IOException
+    {
+        int v = in.read();
+        if (v == -1)
+        {
+            return -1;
+        }
+ 
+        // V was the quote '=' character/
+        while (v == '=')
+        {
+            //
+            // Get the next character.
+            //
+            int j = in.read();
+            if (j == -1)
+            {
+                throw new IllegalStateException("Quoted '=' at end of stream");
+            }
+
+            // For systems generating CRLF line endings.
+            if (j == '\r')
+            {
+                j = in.read();
+                if (j == '\n')
+                {
+                    //
+                    // This was a line break that was not actually a line break in the original information.
+                    // So return the next data.
+                    //
+                    j = in.read();
+                }
+                v = j;
+                continue;
+            }
+            else if (j == '\n')
+            {
+                // As above but without preceding CR.
+                v = in.read();
+                continue;
+            }
+            else
+            {
+
+                int chr = 0;
+
+                if (j >= '0' && j <= '9')
+                {
+                    chr = j - '0';
+                }
+                else if (j >= 'A' && j <= 'F')
+                {
+                    chr = 10 + (j - 'A');
+                }
+                else
+                {
+                    throw new IllegalStateException("Expecting '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF");
+                }
+
+                chr <<= 4;
+
+                j = in.read();
+
+                if (j >= '0' && j <= '9')
+                {
+                    chr |= j - '0';
+                }
+                else if (j >= 'A' && j <= 'F')
+                {
+                    chr |= 10 + (j - 'A');
+                }
+                else
+                {
+                    throw new IllegalStateException("Expecting second '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF");
+                }
+
+                return chr;
+            }
+        }
+
+        return v;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java
new file mode 100644
index 0000000..998c3bc
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMEEnvelopedWriter.java
@@ -0,0 +1,185 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.bouncycastle.cms.CMSAttributeTableGenerator;
+import org.bouncycastle.cms.CMSEnvelopedDataStreamGenerator;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInfoGenerator;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeIOException;
+import org.bouncycastle.mime.MimeWriter;
+import org.bouncycastle.mime.encoding.Base64OutputStream;
+import org.bouncycastle.operator.OutputEncryptor;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Writer for SMIME Enveloped objects.
+ */
+public class SMIMEEnvelopedWriter
+    extends MimeWriter
+{
+    public static class Builder
+    {
+        private static final String[] stdHeaders;
+        private static final String[] stdValues;
+
+        static
+        {
+            stdHeaders = new String[]
+                {
+                    "Content-Type",
+                    "Content-Disposition",
+                    "Content-Transfer-Encoding",
+                    "Content-Description"
+                };
+
+            stdValues = new String[]
+                {
+                    "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data",
+                    "attachment; filename=\"smime.p7m\"",
+                    "base64",
+                    "S/MIME Encrypted Message"
+                };
+        }
+
+        private final CMSEnvelopedDataStreamGenerator envGen = new CMSEnvelopedDataStreamGenerator();
+        private final Map<String, String> headers = new LinkedHashMap<String, String>();
+
+        String contentTransferEncoding = "base64";
+
+        public Builder()
+        {
+            for (int i = 0; i != stdHeaders.length; i++)
+            {
+                headers.put(stdHeaders[i], stdValues[i]);
+            }
+        }
+
+        public Builder setUnprotectedAttributeGenerator(CMSAttributeTableGenerator unprotectedAttributeGenerator)
+        {
+            this.envGen.setUnprotectedAttributeGenerator(unprotectedAttributeGenerator);
+
+            return this;
+        }
+
+        public Builder setOriginatorInfo(OriginatorInformation originatorInfo)
+        {
+            this.envGen.setOriginatorInfo(originatorInfo);
+
+            return this;
+        }
+
+        /**
+         * Specify a MIME header (name, value) pair for this builder. If the headerName already exists it will
+         * be overridden.
+         *
+         * @param headerName name of the MIME header.
+         * @param headerValue value of the MIME header.
+         *
+         * @return the current Builder instance.
+         */
+        public Builder withHeader(String headerName, String headerValue)
+        {
+            this.headers.put(headerName, headerValue);
+
+            return this;
+        }
+        
+        /**
+         * Add a generator to produce the recipient info required.
+         *
+         * @param recipientGenerator a generator of a recipient info object.
+         *
+         * @return the current Builder instance.
+         */
+        public Builder addRecipientInfoGenerator(RecipientInfoGenerator recipientGenerator)
+        {
+            this.envGen.addRecipientInfoGenerator(recipientGenerator);
+
+            return this;
+        }
+
+        public SMIMEEnvelopedWriter build(OutputStream mimeOut, OutputEncryptor outEnc)
+        {
+            return new SMIMEEnvelopedWriter(this, outEnc, mimeOut);
+        }
+    }
+
+    private final CMSEnvelopedDataStreamGenerator envGen;
+
+    private final OutputEncryptor outEnc;
+    private final OutputStream mimeOut;
+    private final String contentTransferEncoding;
+
+    private SMIMEEnvelopedWriter(Builder builder, OutputEncryptor outEnc, OutputStream mimeOut)
+    {
+        super(new Headers(mapToLines(builder.headers), builder.contentTransferEncoding));
+
+        this.envGen = builder.envGen;
+        this.contentTransferEncoding = builder.contentTransferEncoding;
+        this.outEnc = outEnc;
+        this.mimeOut = mimeOut;
+    }
+    
+    public OutputStream getContentStream()
+        throws IOException
+    {
+        headers.dumpHeaders(mimeOut);
+
+        mimeOut.write(Strings.toByteArray("\r\n"));
+
+        try
+        {
+            OutputStream outStream;
+
+            if ("base64".equals(contentTransferEncoding))
+            {
+                outStream = new Base64OutputStream(mimeOut);
+                
+                return new ContentOutputStream(envGen.open(SMimeUtils.createUnclosable(outStream), outEnc), outStream);
+            }
+            else
+            {
+                return new ContentOutputStream(envGen.open(SMimeUtils.createUnclosable(mimeOut), outEnc), null);
+            }
+        }
+        catch (CMSException e)
+        {
+            throw new MimeIOException(e.getMessage(), e);
+        }
+    }
+
+    private class ContentOutputStream
+        extends OutputStream
+    {
+        private final OutputStream main;
+        private final OutputStream backing;
+
+        ContentOutputStream(OutputStream main, OutputStream backing)
+        {
+            this.main = main;
+            this.backing = backing;
+        }
+
+        public void write(int i)
+            throws IOException
+        {
+            main.write(i);
+        }
+
+        public void close()
+            throws IOException
+        {
+            main.close();
+            if (backing != null)
+            {
+                backing.close();
+            }
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMESignedWriter.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMESignedWriter.java
new file mode 100644
index 0000000..86e8361
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMIMESignedWriter.java
@@ -0,0 +1,396 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.math.BigInteger;
+import java.security.SecureRandom;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedDataStreamGenerator;
+import org.bouncycastle.cms.SignerInfoGenerator;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeWriter;
+import org.bouncycastle.mime.encoding.Base64OutputStream;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+
+/**
+ * Writer for SMIME Signed objects.
+ */
+public class SMIMESignedWriter
+    extends MimeWriter
+{
+    public static final Map RFC3851_MICALGS;
+    public static final Map RFC5751_MICALGS;
+    public static final Map STANDARD_MICALGS;
+
+    static
+    {
+        Map stdMicAlgs = new HashMap();
+
+        stdMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        stdMicAlgs.put(CMSAlgorithm.SHA1, "sha-1");
+        stdMicAlgs.put(CMSAlgorithm.SHA224, "sha-224");
+        stdMicAlgs.put(CMSAlgorithm.SHA256, "sha-256");
+        stdMicAlgs.put(CMSAlgorithm.SHA384, "sha-384");
+        stdMicAlgs.put(CMSAlgorithm.SHA512, "sha-512");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        RFC5751_MICALGS = Collections.unmodifiableMap(stdMicAlgs);
+
+        Map oldMicAlgs = new HashMap();
+
+        oldMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        oldMicAlgs.put(CMSAlgorithm.SHA1, "sha1");
+        oldMicAlgs.put(CMSAlgorithm.SHA224, "sha224");
+        oldMicAlgs.put(CMSAlgorithm.SHA256, "sha256");
+        oldMicAlgs.put(CMSAlgorithm.SHA384, "sha384");
+        oldMicAlgs.put(CMSAlgorithm.SHA512, "sha512");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        RFC3851_MICALGS = Collections.unmodifiableMap(oldMicAlgs);
+
+        STANDARD_MICALGS = RFC5751_MICALGS;
+    }
+
+    public static class Builder
+    {
+        private static final String[] detHeaders;
+        private static final String[] detValues;
+        private static final String[] encHeaders;
+        private static final String[] encValues;
+
+        static
+        {
+            detHeaders = new String[]
+                {
+                    "Content-Type"
+                };
+
+            detValues = new String[]
+                {
+                    "multipart/signed; protocol=\"application/pkcs7-signature\"",
+                };
+
+            encHeaders = new String[]
+                {
+                    "Content-Type",
+                    "Content-Disposition",
+                    "Content-Transfer-Encoding",
+                    "Content-Description"
+                };
+
+            encValues = new String[]
+                {
+                    "application/pkcs7-mime; name=\"smime.p7m\"; smime-type=enveloped-data",
+                    "attachment; filename=\"smime.p7m\"",
+                    "base64",
+                    "S/MIME Signed Message"
+                };
+        }
+
+        private final CMSSignedDataStreamGenerator sigGen = new CMSSignedDataStreamGenerator();
+        private final Map<String, String> extraHeaders = new LinkedHashMap<String, String>();
+        private final boolean encapsulated;
+        private final Map micAlgs = STANDARD_MICALGS;
+        
+        String contentTransferEncoding = "base64";
+
+        public Builder()
+        {
+            this(false);
+        }
+
+        public Builder(
+            boolean encapsulated)
+        {
+            this.encapsulated = encapsulated;
+        }
+
+        /**
+         * Specify a MIME header (name, value) pair for this builder. If the headerName already exists it will
+         * be overridden.
+         *
+         * @param headerName  name of the MIME header.
+         * @param headerValue value of the MIME header.
+         * @return the current Builder instance.
+         */
+        public Builder withHeader(String headerName, String headerValue)
+        {
+            this.extraHeaders.put(headerName, headerValue);
+
+            return this;
+        }
+
+        public Builder addCertificate(X509CertificateHolder certificate)
+            throws CMSException
+        {
+            this.sigGen.addCertificate(certificate);
+
+            return this;
+        }
+
+        public Builder addCertificates(Store certificates)
+            throws CMSException
+        {
+            this.sigGen.addCertificates(certificates);
+
+            return this;
+        }
+
+        /**
+         * Add a generator to produce the signer info required.
+         *
+         * @param signerGenerator a generator for a signer info object.
+         * @return the current Builder instance.
+         */
+        public Builder addSignerInfoGenerator(SignerInfoGenerator signerGenerator)
+        {
+            this.sigGen.addSignerInfoGenerator(signerGenerator);
+
+            return this;
+        }
+
+        public SMIMESignedWriter build(OutputStream mimeOut)
+        {
+            Map<String, String> headers = new LinkedHashMap<String, String>();
+
+            String boundary;
+            if (encapsulated)
+            {
+                boundary = null;
+                for (int i = 0; i != encHeaders.length; i++)
+                {
+                    headers.put(encHeaders[i], encValues[i]);
+                }
+            }
+            else
+            {
+                boundary = generateBoundary();
+
+                // handle Content-Type specially
+                StringBuffer contValue = new StringBuffer(detValues[0]);
+
+                addHashHeader(contValue, sigGen.getDigestAlgorithms());
+
+                addBoundary(contValue, boundary);
+                headers.put(detHeaders[0], contValue.toString());
+
+                for (int i = 1; i < detHeaders.length; i++)
+                {
+                    headers.put(detHeaders[i], detValues[i]);
+                }
+            }
+
+            for (Iterator it = extraHeaders.entrySet().iterator(); it.hasNext();)
+            {
+                Map.Entry ent = (Map.Entry)it.next();
+                headers.put((String)ent.getKey(), (String)ent.getValue());
+            }
+
+            return new SMIMESignedWriter(this, headers, boundary, mimeOut);
+        }
+
+        private void addHashHeader(
+            StringBuffer header,
+            List signers)
+        {
+            int count = 0;
+
+            //
+            // build the hash header
+            //
+            Iterator it = signers.iterator();
+            Set micAlgSet = new TreeSet();
+
+            while (it.hasNext())
+            {
+                AlgorithmIdentifier digest = (AlgorithmIdentifier)it.next();
+
+                String micAlg = (String)micAlgs.get(digest.getAlgorithm());
+
+                if (micAlg == null)
+                {
+                    micAlgSet.add("unknown");
+                }
+                else
+                {
+                    micAlgSet.add(micAlg);
+                }
+            }
+
+            it = micAlgSet.iterator();
+
+            while (it.hasNext())
+            {
+                String alg = (String)it.next();
+
+                if (count == 0)
+                {
+                    if (micAlgSet.size() != 1)
+                    {
+                        header.append("; micalg=\"");
+                    }
+                    else
+                    {
+                        header.append("; micalg=");
+                    }
+                }
+                else
+                {
+                    header.append(',');
+                }
+
+                header.append(alg);
+
+                count++;
+            }
+
+            if (count != 0)
+            {
+                if (micAlgSet.size() != 1)
+                {
+                    header.append('\"');
+                }
+            }
+        }
+
+        private void addBoundary(
+             StringBuffer header,
+             String boundary)
+        {
+             header.append(";\r\n\tboundary=\"");
+             header.append(boundary);
+             header.append("\"");
+        }
+
+        private String generateBoundary()
+        {
+            SecureRandom random = new SecureRandom();
+
+            return "==" + new BigInteger(180, random).setBit(179).toString(16) + "=";
+        }
+    }
+
+    private final CMSSignedDataStreamGenerator sigGen;
+
+    private final String boundary;
+    private final OutputStream mimeOut;
+    private final String contentTransferEncoding;
+
+    private SMIMESignedWriter(Builder builder, Map<String, String> headers, String boundary, OutputStream mimeOut)
+    {
+        super(new Headers(mapToLines(headers), builder.contentTransferEncoding));
+
+        this.sigGen = builder.sigGen;
+        this.contentTransferEncoding = builder.contentTransferEncoding;
+        this.boundary = boundary;
+        this.mimeOut = mimeOut;
+    }
+
+    /**
+     * Return a content stream for the signer - note data written to this stream needs to properly
+     * canonicalised if necessary.
+     *
+     * @return an output stream for data to be signed to be written to.
+     * @throws IOException on a stream error.
+     */
+    public OutputStream getContentStream()
+        throws IOException
+    {
+        headers.dumpHeaders(mimeOut);
+
+        mimeOut.write(Strings.toByteArray("\r\n"));
+
+        if (boundary == null)
+        {
+            return null; // TODO: new ContentOutputStream(sigGen.open(mimeOut, true), mimeOut);
+        }
+        else
+        {
+            mimeOut.write(Strings.toByteArray("This is an S/MIME signed message\r\n"));
+            mimeOut.write(Strings.toByteArray("\r\n--"));
+            mimeOut.write(Strings.toByteArray(boundary));
+            mimeOut.write(Strings.toByteArray("\r\n"));
+
+            ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+            Base64OutputStream stream = new Base64OutputStream(bOut);
+
+            return new ContentOutputStream(sigGen.open(stream,false, SMimeUtils.createUnclosable(mimeOut)), mimeOut, bOut, stream);
+        }
+    }
+
+    private class ContentOutputStream
+        extends OutputStream
+    {
+        private final OutputStream main;
+        private final OutputStream backing;
+        private final ByteArrayOutputStream sigStream;
+        private final OutputStream sigBase;
+
+        ContentOutputStream(OutputStream main, OutputStream backing, ByteArrayOutputStream sigStream, OutputStream sigBase)
+        {
+            this.main = main;
+            this.backing = backing;
+            this.sigStream = sigStream;
+            this.sigBase = sigBase;
+        }
+
+        public void write(int i)
+            throws IOException
+        {
+            main.write(i);
+        }
+
+        public void close()
+            throws IOException
+        {
+            if (boundary != null)
+            {
+                main.close();
+
+                backing.write(Strings.toByteArray("\r\n--"));
+                backing.write(Strings.toByteArray(boundary));
+                backing.write(Strings.toByteArray("\r\n"));
+
+                backing.write(Strings.toByteArray("Content-Type: application/pkcs7-signature; name=\"smime.p7s\"\r\n"));
+                backing.write(Strings.toByteArray("Content-Transfer-Encoding: base64\r\n"));
+                backing.write(Strings.toByteArray("Content-Disposition: attachment; filename=\"smime.p7s\"\r\n"));
+                backing.write(Strings.toByteArray("\r\n"));
+
+                if (sigBase != null)
+                {
+                    sigBase.close();
+                }
+                
+                backing.write(sigStream.toByteArray());
+
+                backing.write(Strings.toByteArray("\r\n--"));
+                backing.write(Strings.toByteArray(boundary));
+                backing.write(Strings.toByteArray("--\r\n"));
+            }
+
+            if (backing != null)
+            {
+                backing.close();
+            }
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeMultipartContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeMultipartContext.java
new file mode 100644
index 0000000..3395ab1
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeMultipartContext.java
@@ -0,0 +1,118 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.Map;
+
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.mime.CanonicalOutputStream;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeContext;
+import org.bouncycastle.mime.MimeMultipartContext;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.io.TeeInputStream;
+import org.bouncycastle.util.io.TeeOutputStream;
+
+public class SMimeMultipartContext
+    implements MimeMultipartContext
+{
+    private final SMimeParserContext parserContext;
+
+    private DigestCalculator[] calculators;
+
+
+    public SMimeMultipartContext(MimeParserContext parserContext, Headers headers)
+    {
+        this.parserContext = (SMimeParserContext)parserContext;
+        this.calculators = createDigestCalculators(headers);
+    }
+
+    DigestCalculator[] getDigestCalculators()
+    {
+        return calculators;
+    }
+
+    OutputStream getDigestOutputStream()
+    {
+        if (calculators.length == 1)
+        {
+            return calculators[0].getOutputStream();
+        }
+        else
+        {
+            OutputStream compoundStream = calculators[0].getOutputStream();
+
+            for (int i = 1; i < calculators.length; i++)
+            {
+                compoundStream = new TeeOutputStream(calculators[i].getOutputStream(), compoundStream);
+            }
+
+            return compoundStream;
+        }
+    }
+
+    private DigestCalculator[] createDigestCalculators(Headers headers)
+    {
+        try
+        {
+            Map<String, String> contentTypeFields = headers.getContentTypeAttributes();
+
+            String micalgs = (String)contentTypeFields.get("micalg");
+            if (micalgs == null)
+            {
+                throw new IllegalStateException("No micalg field on content-type header");
+            }
+
+            String[] algs = micalgs.substring(micalgs.indexOf('=') + 1).split(",");
+            DigestCalculator[] dcOut = new DigestCalculator[algs.length];
+
+            for (int t = 0; t < algs.length; t++)
+            {
+                // Deal with possibility of quoted parts, eg  "SHA1","SHA256" etc
+                String alg = SMimeUtils.lessQuotes(algs[t]).trim();
+                dcOut[t] = parserContext.getDigestCalculatorProvider().get(
+                    new AlgorithmIdentifier(SMimeUtils.getDigestOID(alg)));
+            }
+
+            return dcOut;
+        }
+        catch (OperatorCreationException e)
+        {
+            return null;
+        }
+    }
+
+    public MimeContext createContext(final int partNo)
+        throws IOException
+    {
+        return new MimeContext()
+        {
+            public InputStream applyContext(Headers headers, InputStream contentStream)
+                throws IOException
+            {
+                if (partNo == 0)
+                {
+                    OutputStream digestOut = getDigestOutputStream();
+
+                    headers.dumpHeaders(digestOut);
+
+                    digestOut.write('\r');
+                    digestOut.write('\n');
+
+                    return new TeeInputStream(contentStream, new CanonicalOutputStream(parserContext, headers, digestOut));
+                }
+
+                return contentStream;
+            }
+        };
+    }
+
+    public InputStream applyContext(Headers headers, InputStream contentStream)
+        throws IOException
+    {
+        return contentStream;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserContext.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserContext.java
new file mode 100644
index 0000000..f494414
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserContext.java
@@ -0,0 +1,27 @@
+package org.bouncycastle.mime.smime;
+
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+
+public class SMimeParserContext
+    implements MimeParserContext
+{
+    private final String defaultContentTransferEncoding;
+    private final DigestCalculatorProvider digestCalculatorProvider;
+
+    public SMimeParserContext(String defaultContentTransferEncoding, DigestCalculatorProvider digestCalculatorProvider)
+    {
+        this.defaultContentTransferEncoding = defaultContentTransferEncoding;
+        this.digestCalculatorProvider = digestCalculatorProvider;
+    }
+
+    public String getDefaultContentTransferEncoding()
+    {
+        return defaultContentTransferEncoding;
+    }
+
+    public DigestCalculatorProvider getDigestCalculatorProvider()
+    {
+        return digestCalculatorProvider;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserListener.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserListener.java
new file mode 100644
index 0000000..d6e1ee8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserListener.java
@@ -0,0 +1,105 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cms.CMSEnvelopedDataParser;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSSignedData;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.mime.ConstantMimeContext;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeContext;
+import org.bouncycastle.mime.MimeIOException;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserListener;
+import org.bouncycastle.operator.DigestCalculator;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.io.Streams;
+
+public abstract class SMimeParserListener
+    implements MimeParserListener
+{
+    private DigestCalculator[] digestCalculators;
+    private SMimeMultipartContext parent;
+
+    public MimeContext createContext(MimeParserContext parserContext, Headers headers)
+    {
+        if (headers.isMultipart())
+        {
+            parent = new SMimeMultipartContext(parserContext, headers);
+            this.digestCalculators = parent.getDigestCalculators();
+            return parent;
+        }
+        else
+        {
+            return new ConstantMimeContext();
+        }
+    }
+
+    public void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+        throws IOException
+    {
+        try
+        {
+            if (headers.getContentType().equals("application/pkcs7-signature")
+                || headers.getContentType().equals("application/x-pkcs7-signature"))
+            {
+                Map<ASN1ObjectIdentifier, byte[]> hashes = new HashMap<ASN1ObjectIdentifier, byte[]>();
+
+                for (int i = 0; i != digestCalculators.length; i++)
+                {
+                    digestCalculators[i].getOutputStream().close();
+
+                    hashes.put(digestCalculators[i].getAlgorithmIdentifier().getAlgorithm(), digestCalculators[i].getDigest());
+                }
+
+                byte[] sigBlock = Streams.readAll(inputStream);
+
+                CMSSignedData signedData = new CMSSignedData(hashes, sigBlock);
+
+                signedData(parserContext, headers, signedData.getCertificates(), signedData.getCRLs(), signedData.getAttributeCertificates(), signedData.getSignerInfos());
+            }
+            else if (headers.getContentType().equals("application/pkcs7-mime")
+                  || headers.getContentType().equals("application/x-pkcs7-mime"))
+            {
+                CMSEnvelopedDataParser envelopedDataParser = new CMSEnvelopedDataParser(inputStream);
+
+                envelopedData(parserContext, headers, envelopedDataParser.getOriginatorInfo(), envelopedDataParser.getRecipientInfos());
+
+                envelopedDataParser.close();
+            }
+            else
+            {
+                content(parserContext, headers, inputStream);
+            }
+        }
+        catch (CMSException e)
+        {
+            throw new MimeIOException("CMS failure: " + e.getMessage(), e);
+        }
+    }
+
+    public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+        throws IOException
+    {
+        throw new IllegalStateException("content handling not implemented");
+    }
+
+    public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+        throws IOException, CMSException
+    {
+        throw new IllegalStateException("signedData handling not implemented");
+    }
+
+    public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originatorInformation, RecipientInformationStore recipients)
+        throws IOException, CMSException
+    {
+        throw new IllegalStateException("envelopedData handling not implemented");
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserProvider.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserProvider.java
new file mode 100644
index 0000000..5c5bba6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeParserProvider.java
@@ -0,0 +1,35 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.bouncycastle.mime.BasicMimeParser;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.operator.DigestCalculatorProvider;
+
+public class SMimeParserProvider
+    implements MimeParserProvider
+{
+    private final String defaultContentTransferEncoding;
+    private final DigestCalculatorProvider digestCalculatorProvider;
+
+    public SMimeParserProvider(String defaultContentTransferEncoding, DigestCalculatorProvider digestCalculatorProvider)
+    {
+        this.defaultContentTransferEncoding = defaultContentTransferEncoding;
+        this.digestCalculatorProvider = digestCalculatorProvider;
+    }
+
+    public MimeParser createParser(InputStream source)
+        throws IOException
+    {
+        return new BasicMimeParser(new SMimeParserContext(defaultContentTransferEncoding, digestCalculatorProvider), source);
+    }
+
+    public MimeParser createParser(Headers headers, InputStream source)
+        throws IOException
+    {
+        return new BasicMimeParser(new SMimeParserContext(defaultContentTransferEncoding, digestCalculatorProvider), headers, source);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeUtils.java b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeUtils.java
new file mode 100644
index 0000000..6f9daeb
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/smime/SMimeUtils.java
@@ -0,0 +1,135 @@
+package org.bouncycastle.mime.smime;
+
+import java.io.FilterOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.util.Strings;
+
+class SMimeUtils
+{
+    private static final Map RFC5751_MICALGS;
+    private static final Map RFC3851_MICALGS;
+    private static final Map STANDARD_MICALGS;
+    private static final Map forMic;
+
+    private static final byte[] nl = new byte[2];
+
+
+    static
+    {
+        nl[0] = '\r';
+        nl[1] = '\n';
+
+
+        Map stdMicAlgs = new HashMap();
+
+        stdMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        stdMicAlgs.put(CMSAlgorithm.SHA1, "sha-1");
+        stdMicAlgs.put(CMSAlgorithm.SHA224, "sha-224");
+        stdMicAlgs.put(CMSAlgorithm.SHA256, "sha-256");
+        stdMicAlgs.put(CMSAlgorithm.SHA384, "sha-384");
+        stdMicAlgs.put(CMSAlgorithm.SHA512, "sha-512");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        stdMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+        RFC5751_MICALGS = Collections.unmodifiableMap(stdMicAlgs);
+
+        Map oldMicAlgs = new HashMap();
+
+        oldMicAlgs.put(CMSAlgorithm.MD5, "md5");
+        oldMicAlgs.put(CMSAlgorithm.SHA1, "sha1");
+        oldMicAlgs.put(CMSAlgorithm.SHA224, "sha224");
+        oldMicAlgs.put(CMSAlgorithm.SHA256, "sha256");
+        oldMicAlgs.put(CMSAlgorithm.SHA384, "sha384");
+        oldMicAlgs.put(CMSAlgorithm.SHA512, "sha512");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411, "gostr3411-94");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_256, "gostr3411-2012-256");
+        oldMicAlgs.put(CMSAlgorithm.GOST3411_2012_512, "gostr3411-2012-512");
+
+
+        RFC3851_MICALGS = Collections.unmodifiableMap(oldMicAlgs);
+
+        STANDARD_MICALGS = RFC5751_MICALGS;
+
+
+        Map<String, ASN1ObjectIdentifier> mic = new TreeMap<String, ASN1ObjectIdentifier>(String.CASE_INSENSITIVE_ORDER);
+
+        for (Iterator it = STANDARD_MICALGS.keySet().iterator(); it.hasNext();)
+        {
+            Object key = it.next();
+            mic.put(STANDARD_MICALGS.get(key).toString(), (ASN1ObjectIdentifier)key);
+        }
+
+        for (Iterator it = RFC3851_MICALGS.keySet().iterator(); it.hasNext();)
+        {
+            Object key = it.next();
+            mic.put(RFC3851_MICALGS.get(key).toString(), (ASN1ObjectIdentifier)key);
+        }
+
+        forMic = Collections.unmodifiableMap(mic);
+
+    }
+
+    static String lessQuotes(String in)
+    {
+        if (in == null || in.length() <= 1)  // make sure we have at least 2 characters
+        {
+            return in;
+        }
+
+        if (in.charAt(0) == '"' && in.charAt(in.length() - 1) == '"')
+        {
+            return in.substring(1, in.length() - 1);
+        }
+
+        return in;
+    }
+
+    static String getParameter(String startsWith, List<String> parameters)
+    {
+        for (Iterator<String> paramIt = parameters.iterator(); paramIt.hasNext(); )
+        {
+            String param = (String)paramIt.next();
+            if (param.startsWith(startsWith))
+            {
+                return param;
+            }
+        }
+
+        return null;
+    }
+
+    static ASN1ObjectIdentifier getDigestOID(String alg)
+    {
+        ASN1ObjectIdentifier oid = (ASN1ObjectIdentifier)forMic.get(Strings.toLowerCase(alg));
+
+        if (oid == null)
+        {
+            throw new IllegalArgumentException("unknown micalg passed: " + alg);
+        }
+
+        return oid;
+    }
+
+    static OutputStream createUnclosable(OutputStream destination)
+    {
+        return new FilterOutputStream(destination)
+        {
+            public void close()
+                throws IOException
+            {
+
+            }
+        };
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/AllTests.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/AllTests.java
new file mode 100644
index 0000000..5c17aa6
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/AllTests.java
@@ -0,0 +1,47 @@
+package org.bouncycastle.mime.test;
+
+import java.security.Security;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+public class AllTests
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    public void setUp()
+    {
+        if (Security.getProvider(BC) != null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        Security.addProvider(new BouncyCastleProvider());
+
+        junit.textui.TestRunner.run(suite());
+    }
+
+    public static Test suite()
+        throws Exception
+    {
+        TestSuite suite = new TestSuite("MIME tests");
+
+        suite.addTestSuite(Base64TransferEncodingTest.class);
+        suite.addTestSuite(MimeParserTest.class);
+        suite.addTestSuite(MultipartParserTest.class);
+        suite.addTestSuite(QuotedPrintableTest.class);
+        suite.addTestSuite(TestBoundaryLimitedInputStream.class);
+        suite.addTestSuite(TestSMIMEEnveloped.class);
+        suite.addTestSuite(TestSMIMESigned.class);
+        suite.addTestSuite(TestSMIMESignEncrypt.class);
+
+        return new MIMETestSetup(suite);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/Base64TransferEncodingTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/Base64TransferEncodingTest.java
new file mode 100644
index 0000000..cf765e2
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/Base64TransferEncodingTest.java
@@ -0,0 +1,266 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.SecureRandom;
+
+import junit.framework.TestCase;
+import org.bouncycastle.util.encoders.Base64;
+
+public class Base64TransferEncodingTest
+    extends TestCase
+{
+    private SecureRandom random = new SecureRandom();
+
+    /**
+     * Test the decoding of some base64 arranged in lines of
+     * 64 byte base 64 encoded rows terminated CRLF.
+     *
+     * @throws Exception
+     */
+    public void testDecodeWellFormed()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+            
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\r');
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * Test decode without CR only LF.
+     *
+     * @throws Exception
+     */
+    public void testDecodeWithoutCR()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * Test decode with long lines past the length in the spec.
+     *
+     * @throws Exception
+     */
+    public void testDecodeLongLines()
+        throws Exception
+    {
+        byte[][] original = new byte[4][765];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 1023 bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * Test decode with long lines past the length in the spec.
+     *
+     * @throws Exception
+     */
+    public void testExcessiveLongLine()
+        throws Exception
+    {
+        byte[][] original = new byte[4][766];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 1023 bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\n');
+        }
+
+        try
+        {
+            verifyDecode(original, bos);
+        }
+        catch (Exception ex)
+        {
+            TestCase.assertEquals("End of line of base64 not reached before line buffer overflow.", ex.getMessage());
+        }
+    }
+
+
+    /**
+     * Test decode of empty data.
+     *
+     * @throws Exception
+     */
+    public void testEmpty()
+        throws Exception
+    {
+        // Assertions in verifyDecode()
+        verifyDecode(new byte[0][0], new ByteArrayOutputStream());
+    }
+
+
+    private void verifyDecode(byte[][] original, ByteArrayOutputStream bos)
+        throws IOException
+    {
+//        MimeParserInputStream source = new MimeParserInputStream(new ByteArrayInputStream(bos.toByteArray()), 1024);
+//        Base64TransferDecoder bte = new Base64TransferDecoder(source, 1024);
+//
+//        for (byte[] row : original)
+//        {
+//            for (byte expected : row)
+//            {
+//                TestCase.assertEquals(expected & 0xFF, bte.read());
+//            }
+//        }
+//
+//        TestCase.assertEquals(-1, bte.read());
+
+    }
+
+
+    /**
+     * This test causes the final line of base64 to not be a multiple of 64.
+     *
+     * @throws Exception
+     */
+    public void testDecodeLengths()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+        original[original.length - 1] = new byte[22];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\r');
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    /**
+     * This test causes the final line of base64 to not be a multiple of 64.
+     *
+     * @throws Exception
+     */
+    public void testPartialLineEnding()
+        throws Exception
+    {
+        byte[][] original = new byte[4][48];
+        original[original.length - 1] = new byte[22];
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        //
+        // Create 4 lines of 64bytes of base64 encoded data.
+        //
+        for (int i = 0; i != original.length; i++)
+        {
+            byte[] row = original[i];
+
+            random.nextBytes(row);
+            bos.write(Base64.encode(row));
+            bos.write('\r');
+            bos.write('\n');
+        }
+
+        verifyDecode(original, bos);
+    }
+
+
+    public void testMultilined()
+        throws Exception
+    {
+        String b64 = "MIAGCSqGSIb3DQEHA6CAMIACAQAxggFOMIIBSgIBADCBsjCBrDELMAkGA1UEBhMCQVQxEDAOBgNV\n" +
+            "BAgTB0F1c3RyaWExDzANBgNVBAcTBlZpZW5uYTEaMBgGA1UEChMRVGlhbmkgU3Bpcml0IEdtYkgx\n" +
+            "GTAXBgNVBAsTEERlbW8gRW52aXJvbm1lbnQxEDAOBgNVBAMTB1Rlc3QgQ0ExMTAvBgkqhkiG9w0B\n" +
+            "CQEWIm1hc3NpbWlsaWFuby5tYXNpQHRpYW5pLXNwaXJpdC5jb20CAQkwDQYJKoZIhvcNAQEBBQAE\n" +
+            "gYALxKaiVW43jHjDiJ4kC6N90lpyG0jxeJ7nynWaR4YkDiUQ/jE8cJwRX0jBQeWKRvf3Y+XhRuB3\n" +
+            "B76cKxBGTgMh6pCuLoIvgBJq54kqql/xz3hO7QRvvuHnEljlw2uhd0PQqQYe8oLdu1Yqyo9+9Jsx\n" +
+            "I7QX43E2H5b3nNGND24djDCABgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBD+UNge0S52HEPuFBEq\n" +
+            "IEvYoIAEggHAcOET1XS7H/OZALZ0cyns3p6kxgAlblE4BvMQnAen8VlhDehp130WdDF4jC+zRjza\n" +
+            "ZftPatKq/Hlhu0wuj+FZESjy2d2hR7FT8qCqGda70IyyOhloG7Ym+17E0MyYQsH38i+uC8NjcSeo\n" +
+            "egggsQoidePpg/9BNFMA4j6vORFcNBvnwj71mV2icx7mUud97cXobJnrfm3hmEmYkm7wL413cibH\n" +
+            "b8K3yNu/hMqJViT0GvlhQdR9hDgu5i2WhiE2UTaFu3xL2xNhzXBvhOwj/gikzFIWva4S/2JfK3M8\n" +
+            "A0lYu6f1vYUF2jazi81wQFEF7qKyp7zx7X2iZjn8DDSCY73izHafF1JJijDFaHrD5245kaSJ7MKP\n" +
+            "jJ/HWk9lbed0ay8f96QuvWEEKSy4xejy6w7DKxKr4icN7KDE5Nyc2ZAJxmCm50B7yHpNZfKQ38E+\n" +
+            "e/bCgvAESFcnw9pRJz9mXmwazxEvCpoO/ezgmgro+59CCRKqdUeOyyLQg6d7xqUcgeY1SoDxzEre\n" +
+            "i4IBlig6+HWLs+9OPMa2fuYYIVZvg7mpeM4lEfdhRssWBWwTTmrtwRbAaT7BTCtlvfqzpHrycp5O\n" +
+            "zgAAAAAAAAAAAAA=";
+
+
+        byte[] data = Base64.decode(b64);
+
+//
+//        MimeParserInputStream mpin = new MimeParserInputStream(new ByteArrayInputStream(b64.getBytes()), 1024);
+//        Base64TransferDecoder btd = new Base64TransferDecoder(mpin, 1024);
+//
+//
+//        for (int t = 0; t < data.length; t++)
+//        {
+//            TestCase.assertEquals("Position: " + t, data[t] & 0xFF, btd.read());
+//        }
+//
+//        TestCase.assertEquals(-1, btd.read());
+
+    }
+
+}
+
+
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/MIMETestSetup.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/MIMETestSetup.java
new file mode 100644
index 0000000..17e8484
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/MIMETestSetup.java
@@ -0,0 +1,28 @@
+
+package org.bouncycastle.mime.test;
+
+import java.security.Security;
+
+import junit.extensions.TestSetup;
+import junit.framework.Test;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+
+class MIMETestSetup
+    extends TestSetup
+{
+    public MIMETestSetup(Test test)
+    {
+        super(test);
+    }
+
+    protected void setUp()
+    {
+        Security.addProvider(new BouncyCastleProvider());
+    }
+
+    protected void tearDown()
+    {
+        Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME);
+    }
+
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/MimeParserTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/MimeParserTest.java
new file mode 100644
index 0000000..9519569
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/MimeParserTest.java
@@ -0,0 +1,45 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+
+import junit.framework.TestCase;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.util.Strings;
+
+public class MimeParserTest
+    extends TestCase
+{
+    public void testMixtureOfHeaders()
+        throws Exception
+    {
+
+        String parts[] = new String[]{
+            "Received", "from mr11p26im-asmtp003.me.com (mr11p26im-asmtp003.me.com [17.110.86.110]) " +
+            "by tauceti.org.au (Our Mail Server) with ESMTP (TLS) id 23294071-1879654 " +
+            "for <megan@cryptoworkshop.com>; Fri, 29 Jun 2018 14:52:26 +1000\n",
+            "Return-Path", " <pogobot@icloud.com>\n",
+            "X-Verify-SMTP", " Host 17.110.86.110 sending to us was not listening\r\n"
+        };
+
+
+        String values = parts[0] + ":" + parts[1] + parts[2] + ":" + parts[3] + parts[4] + ":" + parts[5] + "\r\n";
+
+        Headers headers = new Headers(new ByteArrayInputStream(Strings.toByteArray(values)), "7bit");
+
+        for (int t = 0; t < parts.length; t += 2)
+        {
+            TestCase.assertEquals("Part " + t, parts[t + 1].trim(), headers.getValues(parts[t])[0]);
+        }
+
+    }
+
+    public void testEndOfHeaders()
+        throws Exception
+    {
+        String values = "Foo: bar\r\n\r\n";
+
+        Headers headers = new Headers(new ByteArrayInputStream(Strings.toByteArray(values)), "7bit");
+
+        assertEquals("bar", headers.getValues("Foo")[0]);
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/MultipartParserTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/MultipartParserTest.java
new file mode 100644
index 0000000..ec8ea48
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/MultipartParserTest.java
@@ -0,0 +1,447 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.BasicMimeParser;
+import org.bouncycastle.mime.ConstantMimeContext;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeContext;
+import org.bouncycastle.mime.MimeMultipartContext;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserListener;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.io.Streams;
+
+public class MultipartParserTest
+    extends TestCase
+{
+
+    protected void setUp()
+        throws Exception
+    {
+        if (Security.getProvider("BC") == null)
+        {
+            Security.addProvider(new BouncyCastleProvider());
+        }
+    }
+
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_wellformed()
+        throws Exception
+    {
+        String value = "multipart/alternative;\n" +
+            " boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\"";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/alternative", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(1, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\"}", fieldValues.toString());
+    }
+
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_wellformed_multi()
+        throws Exception
+    {
+        String value = "multipart/signed;\n" +
+            " boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\"; micalg=\"SHA1\"";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/signed", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(2, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"Apple-Mail=_8B1F6ECB-9629-424B-B871-1357CCDBCC84\", micalg=\"SHA1\"}", fieldValues.toString());
+    }
+
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_broken()
+        throws Exception
+    {
+
+        // Verify limit checking
+
+        String value = "multipart/alternative;\n" +
+            " boundary=\"cats\"; micalg=";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/alternative", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(2, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"cats\", micalg=}", fieldValues.toString());
+    }
+
+    /**
+     * Parse content header good.
+     *
+     * @throws Exception
+     */
+    public void testParseContentTypeHeader_empty_micalg()
+        throws Exception
+    {
+
+        // Verify limit checking
+
+        String value = "multipart/alternative;\n" +
+            " boundary=\"cats\"; micalg=\"\"";
+
+        ArrayList<String> values = new ArrayList<String>();
+        values.add("Content-type: " + value);
+
+        Headers headers = new Headers(values, value);
+        TestCase.assertEquals("multipart/alternative", headers.getContentType());
+        Map<String, String> fieldValues = headers.getContentTypeAttributes();
+        TestCase.assertEquals(2, fieldValues.size());
+        TestCase.assertEquals("{boundary=\"cats\", micalg=\"\"}", headers.getContentTypeAttributes().toString());
+    }
+
+    public void testSignedMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("quotable.message"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                Streams.pipeAll((InputStream)inputStream, bos);
+                results.add(bos.toString());
+                System.out.println("#######################################################################");
+                System.out.println(bos.toString());
+                System.out.println("#######################################################################");
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+
+                dataParsed.markDone();
+            }
+
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+
+    public void testInvalidSha256SignedMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("3nnn_smime.eml"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                Streams.pipeAll((InputStream)inputStream, bos);
+                results.add(bos.toString());
+                System.out.println("#######################################################################");
+                System.out.println(bos.toString());
+                System.out.println("#######################################################################");
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        // in this case the signature is invalid
+                        assertEquals(false, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        });
+    }
+
+    public void testEmbeddedMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("embeddedmulti.message"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                Streams.pipeAll((InputStream)inputStream, bos);
+                results.add(bos.toString());
+                System.out.println("#######################################################################");
+                System.out.println(bos.toString());
+                System.out.println("#######################################################################");
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        });
+    }
+
+    public void testMultipartAlternative()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(this.getClass().getResourceAsStream("multi-alternative.eml"));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+
+                MimeParser basicMimeParser = new BasicMimeParser(parserContext, headers, inputStream);
+
+                basicMimeParser.parse(new MimeParserListener()
+                {
+                    public MimeContext createContext(MimeParserContext parserContext, Headers headers)
+                    {
+                        return new ConstantMimeContext();
+                    }
+
+                    public void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                        throws IOException
+                    {
+                        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+                        Streams.pipeAll((InputStream)inputStream, bos);
+                        results.add(bos.toString());
+                        System.out.println("#######################################################################");
+                        System.out.println(bos.toString());
+                        System.out.println("#######################################################################");
+                    }
+                });
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws CMSException
+            {
+                Collection c = signers.getSigners();
+                Iterator it = c.iterator();
+
+                while (it.hasNext())
+                {
+                    SignerInformation signer = (SignerInformation)it.next();
+                    Collection certCollection = certificates.getMatches(signer.getSID());
+
+                    Iterator certIt = certCollection.iterator();
+                    X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                    try
+                    {
+                        assertEquals(true, signer.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                    }
+                    catch (OperatorCreationException e)
+                    {
+                        e.printStackTrace();
+                    }
+                    catch (CertificateException e)
+                    {
+                        e.printStackTrace();
+                    }
+                }
+            }
+
+        });
+    }
+
+    /**
+     * Happy path mime multipart test.
+     *
+     * @throws IOException
+     */
+    public void testMimeMultipart()
+        throws Exception
+    {
+        final ArrayList<Object> results = new ArrayList<Object>();
+
+        BasicMimeParser p = new BasicMimeParser(this.getClass().getResourceAsStream("simplemultipart.eml"));
+
+        p.parse(new MimeParserListener()
+        {
+            public MimeContext createContext(MimeParserContext parserContext, Headers headers)
+            {
+                return new MimeMultipartContext()
+                {
+                    public InputStream applyContext(Headers headers, InputStream contentStream)
+                        throws IOException
+                    {
+                        return contentStream;
+                    }
+
+                    public MimeContext createContext(int partNo)
+                        throws IOException
+                    {
+                        return new MimeContext()
+                        {
+                            public InputStream applyContext(Headers headers, InputStream contentStream)
+                                throws IOException
+                            {
+                                return contentStream;
+                            }
+                        };
+                    }
+                };
+            }
+
+            public void object(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                results.add(Strings.fromByteArray(Streams.readAll(inputStream)));
+            }
+        });
+
+
+        String[] expected = new String[]{
+            "The cat sat on the mat\n" +
+                "\n" +
+                "Boo!\n" +
+                "\n",
+            "<html><head><meta http-equiv=\"Content-Type\" object=\"text/html; charset=us-ascii\"></head><object style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\"><meta http-equiv=\"Content-Type\" object=\"text/html; charset=us-ascii\" class=\"\"><div style=\"word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;\" class=\"\">The cat sat on the mat<div class=\"\"><br class=\"\"></div><div class=\"\"><font size=\"7\" class=\"\">Boo!</font></div><div class=\"\"><font size=\"7\" class=\"\"><br class=\"\"></font></div><div class=\"\"><img src=\"http://img2.thejournal.ie/inline/1162441/original/?width=630&amp;version=1162441\" alt=\"Image result for cows\" class=\"\"></div></div></object></html>"
+        };
+
+        TestCase.assertEquals("Size same:", expected.length, results.size());
+
+        for (int t = 0; t < results.size(); t++)
+        {
+            TestCase.assertEquals("Part: " + t, expected[t], results.get(t));
+        }
+
+    }
+
+
+
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/QuotedPrintableTest.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/QuotedPrintableTest.java
new file mode 100644
index 0000000..22fae1a
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/QuotedPrintableTest.java
@@ -0,0 +1,150 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import junit.framework.TestCase;
+import org.bouncycastle.mime.encoding.QuotedPrintableInputStream;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.io.Streams;
+
+public class QuotedPrintableTest
+    extends TestCase
+{
+    public void testQuotedPrintable()
+        throws IOException
+    {
+        String qp = "J'interdis aux marchands de vanter trop leur marchandises. Car ils se font =\n" +
+            "vite p=C3=A9dagogues et t'enseignent comme but ce qui n'est par essence qu'=\n" +
+            "un moyen, et te trompant ainsi sur la route =C3=A0 suivre les voil=C3=A0 bi=\n" +
+            "ent=C3=B4t qui te d=C3=A9gradent, car si leur musique est vulgaire ils te f=\n" +
+            "abriquent pour te la vendre une =C3=A2me vulgaire."; // From wikipedia.
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(qpd, bos);
+
+        TestCase.assertEquals("J'interdis aux marchands de vanter trop leur marchandises. Car ils se font vite pédagogues et t'enseignent comme but ce qui n'est par essence qu'un moyen, et te trompant ainsi sur la route à suivre les voilà bientôt qui te dégradent, car si leur musique est vulgaire ils te fabriquent pour te la vendre une âme vulgaire.", bos.toString());
+    }
+
+    public void testCRLFHandling()
+        throws Exception
+    {
+        // Some client use CR others use CRLF.
+
+        String qp = "The cat sat =\r\non the mat";
+        String expected = "The cat sat on the mat";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(qpd, bos);
+
+
+        TestCase.assertEquals(expected, bos.toString());
+
+    }
+
+    public void testLFHandling()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =\non the mat";
+        String expected = "The cat sat on the mat";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(qpd, bos);
+
+        TestCase.assertEquals(expected, bos.toString());
+    }
+
+    /**
+     * No character after '='.
+     *
+     * @throws Exception
+     */
+    public void testInvalid_1()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =";
+
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        try
+        {
+            Streams.pipeAll(qpd, bos);
+            TestCase.fail("Must fail!");
+        }
+        catch (Throwable ioex)
+        {
+            TestCase.assertEquals("Quoted '=' at end of stream", ioex.getMessage());
+        }
+    }
+
+    /**
+     * Not hex digit on first character.
+     *
+     * @throws Exception
+     */
+    public void testInvalid_2()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =Z";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        try
+        {
+            Streams.pipeAll(qpd, bos);
+            TestCase.fail("Must fail!");
+        }
+        catch (Throwable ioex)
+        {
+            TestCase.assertEquals("Expecting '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF", ioex.getMessage());
+        }
+    }
+
+    /**
+     * Not hex digit on second character.
+     *
+     * @throws Exception
+     */
+    public void testInvalid_3()
+        throws Exception
+    {
+
+        // Some client use CRLF others just use LF.
+
+        String qp = "The cat sat =AZ";
+
+        QuotedPrintableInputStream qpd = new QuotedPrintableInputStream(new ByteArrayInputStream(Strings.toByteArray(qp)));
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+        try
+        {
+            Streams.pipeAll(qpd, bos);
+            TestCase.fail("Must fail!");
+        }
+        catch (Throwable ioex)
+        {
+            TestCase.assertEquals("Expecting second '0123456789ABCDEF after quote that was not immediately followed by LF or CRLF", ioex.getMessage());
+        }
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/ReadOnceInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/ReadOnceInputStream.java
new file mode 100644
index 0000000..d5f59b9
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/ReadOnceInputStream.java
@@ -0,0 +1,58 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+
+/**
+ * File to guarantee no back tracking...
+ */
+public class ReadOnceInputStream
+    extends ByteArrayInputStream
+{
+    public ReadOnceInputStream(byte[] buf)
+    {
+        super(buf);
+    }
+
+    public boolean markSupported()
+    {
+        return false;
+    }
+
+    int currPos = -22;
+
+    public int read()
+    {
+        if (0 > currPos)
+        {
+            currPos = 0;
+        }
+        currPos++;
+
+        return super.read();
+    }
+
+    public int read(byte b[], int off, int len)
+    {
+        if (off < currPos)
+        {
+            throw new RuntimeException("off " + off + " > currPos " + currPos);
+        }
+        currPos = off;
+        int res = super.read(b, off, len);
+        if (res < 0)
+        {
+            throw new RuntimeException("off " + off + " > currPos " + currPos + " res " + res);
+        }
+        currPos += res;
+        return res;
+    }
+
+    public int read(byte b[])
+        throws IOException
+    {
+        int res = super.read(b);
+        currPos += res;
+        return res;
+    }
+}
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestBoundaryLimitedInputStream.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestBoundaryLimitedInputStream.java
new file mode 100644
index 0000000..fd0b637
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestBoundaryLimitedInputStream.java
@@ -0,0 +1,80 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+
+import junit.framework.TestCase;
+import org.bouncycastle.mime.BoundaryLimitedInputStream;
+import org.bouncycastle.util.io.Streams;
+
+public class TestBoundaryLimitedInputStream
+    extends TestCase
+{
+    public void testBoundaryAfterCRLF()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\r\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin, "banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+
+    public void testBoundaryAfterCRLFTrailingLineInContent()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep\r\n";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\r\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin, "banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+
+    public void testBoundaryAfterLF()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin, "banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+
+    public void testBoundaryAfterLFTrailingLine()
+        throws Exception
+    {
+        String data = "The cat sat on the mat\r\n" +
+            "then it went to sleep\n";
+
+
+        ByteArrayInputStream bin = new ByteArrayInputStream((data + "\n--banana").getBytes());
+
+        BoundaryLimitedInputStream blin = new BoundaryLimitedInputStream(bin,"banana");
+
+        ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        Streams.pipeAll(blin, bos);
+
+        TestCase.assertEquals(data, bos.toString());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestDoneFlag.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestDoneFlag.java
new file mode 100644
index 0000000..504ee9e
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestDoneFlag.java
@@ -0,0 +1,16 @@
+package org.bouncycastle.mime.test;
+
+class TestDoneFlag
+{
+    private boolean done = false;
+
+    void markDone()
+    {
+        done = true;
+    }
+
+    boolean isDone()
+    {
+        return done;
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMEEnveloped.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMEEnveloped.java
new file mode 100644
index 0000000..0001cd8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMEEnveloped.java
@@ -0,0 +1,187 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.PrivateKey;
+import java.security.Security;
+import java.security.cert.CertificateFactory;
+import java.security.cert.X509Certificate;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.OriginatorInformation;
+import org.bouncycastle.cms.RecipientInformation;
+import org.bouncycastle.cms.RecipientInformationStore;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.cms.test.CMSTestUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMIMEEnvelopedWriter;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.openssl.PEMKeyPair;
+import org.bouncycastle.openssl.PEMParser;
+import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.Streams;
+
+public class TestSMIMEEnveloped
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    private static String          _signDN;
+    private static KeyPair          _signKP;
+
+    private static String          _reciDN;
+    private static KeyPair          _reciKP;
+
+    private static X509Certificate _reciCert;
+
+    private static boolean         _initialised = false;
+
+    private static final byte[] testMessage = Base64.decode(
+        "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+        "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+        "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+        "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+        "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+        "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+        "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+        "wMTMyLS0NCg==");
+
+    private static void init()
+        throws Exception
+    {
+        if (!_initialised)
+        {
+            if (Security.getProvider("BC") == null)
+            {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+
+            _initialised = true;
+
+            _signDN   = "O=Bouncy Castle, C=AU";
+            _signKP   = CMSTestUtil.makeKeyPair();
+
+            _reciDN   = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+            _reciKP   = CMSTestUtil.makeKeyPair();
+            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+        }
+    }
+
+    public void setUp()
+        throws Exception
+    {
+        init();
+    }
+    
+    public void testSMIMEEnveloped()
+        throws Exception
+    {
+        InputStream inputStream = this.getClass().getResourceAsStream("test256.message");
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(Streams.readAll(inputStream)));
+
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        p.parse(new SMimeParserListener()
+        {
+            public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originator, RecipientInformationStore recipients)
+                throws IOException, CMSException
+            {
+                RecipientInformation recipInfo = recipients.get(new JceKeyTransRecipientId(loadCert("cert.pem")));
+
+                assertNotNull(recipInfo);
+
+                byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(loadKey("key.pem")));
+                assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+
+                dataParsed.markDone();
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+
+    public void testKeyTransAES128()
+        throws Exception
+    {
+        //
+        // output
+        //
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        SMIMEEnvelopedWriter.Builder envBldr = new SMIMEEnvelopedWriter.Builder();
+
+        envBldr.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+        SMIMEEnvelopedWriter envWrt = envBldr.build(bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        OutputStream out = envWrt.getContentStream();
+
+        out.write(testMessage);
+
+        out.close();
+        
+        //
+        // parse
+        //
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(bOut.toByteArray()));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originator, RecipientInformationStore recipients)
+                throws IOException, CMSException
+            {
+                RecipientInformation recipInfo = recipients.get(new JceKeyTransRecipientId(_reciCert));
+
+                assertNotNull(recipInfo);
+
+                byte[] content = recipInfo.getContent(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()));
+                assertTrue(org.bouncycastle.util.Arrays.areEqual(testMessage, content));
+
+                dataParsed.markDone();
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+
+    private X509Certificate loadCert(String name)
+        throws IOException
+    {
+        try
+        {
+            return (X509Certificate)CertificateFactory.getInstance("X.509", "BC").generateCertificate(getClass().getResourceAsStream(name));
+        }
+        catch (Exception e)
+        {
+            throw new IOException(e.getMessage());
+        }
+    }
+
+    private PrivateKey loadKey(String name)
+        throws IOException
+    {
+        return new JcaPEMKeyConverter().setProvider("BC").getKeyPair((PEMKeyPair)(new PEMParser(new InputStreamReader(getClass().getResourceAsStream(name)))).readObject()).getPrivate();
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESignEncrypt.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESignEncrypt.java
new file mode 100644
index 0000000..8018cf8
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESignEncrypt.java
@@ -0,0 +1,227 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSAlgorithm;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.CMSTypedStream;
+import org.bouncycastle.cms.OriginatorInformation;
+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.JcaSignerId;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.jcajce.JceCMSContentEncryptorBuilder;
+import org.bouncycastle.cms.jcajce.JceKeyTransEnvelopedRecipient;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientId;
+import org.bouncycastle.cms.jcajce.JceKeyTransRecipientInfoGenerator;
+import org.bouncycastle.cms.test.CMSTestUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMIMEEnvelopedWriter;
+import org.bouncycastle.mime.smime.SMIMESignedWriter;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.Streams;
+
+public class TestSMIMESignEncrypt
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    private static String _signDN;
+    private static KeyPair _signKP;
+
+    private static String _reciDN;
+    private static KeyPair _reciKP;
+
+    private static X509Certificate _signCert;
+    private static X509Certificate _reciCert;
+
+    private static boolean _initialised = false;
+
+    private static final byte[] simpleMessage = Strings.toByteArray(
+        "Content-Type: text/plain; name=null; charset=us-ascii\r\n" +
+            "Content-Transfer-Encoding: 7bit\r\n" +
+            "Content-Disposition: inline; filename=null\r\n" +
+            "\r\n" +
+            "Hello, world!\r\n");
+
+    private static final byte[] simpleMessageContent = Strings.toByteArray(
+        "Hello, world!\r\n");
+
+    private static final byte[] testMultipartMessage = Base64.decode(
+        "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+            "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+            "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+            "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+            "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+            "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+            "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+            "wMTMyLS0NCg==");
+
+    private static final byte[] testMultipartMessageContent = Base64.decode(
+        "LS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzINCkNvbnRlbnQtVHlwZTogdGV4dC9w" +
+            "bGFpbjsgbmFtZT1udWxsOyBjaGFyc2V0PXVzLWFzY2lpDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5n" +
+            "OiA3Yml0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBpbmxpbmU7IGZpbGVuYW1lPW51bGwNCg0KQ2lhbyBm" +
+            "cm9tIHZpZW5uYQ0KLS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzItLQ0K");
+
+    private static void init()
+        throws Exception
+    {
+        if (!_initialised)
+        {
+            if (Security.getProvider("BC") == null)
+            {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+
+            _initialised = true;
+
+            //create certificate of the sender(signature certificate)
+            _signDN = "O=Bouncy Castle, C=AU";
+            _signKP = CMSTestUtil.makeKeyPair();
+            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
+
+            //create certificate of the receiver (encryption certificate)
+            _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+            _reciKP = CMSTestUtil.makeKeyPair();
+            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+        }
+    }
+
+    public void setUp()
+        throws Exception
+    {
+        init();
+    }
+
+    public void testSignThenEncrypt()
+        throws Exception
+    { 
+  	
+    	//output that will contain signed and encrypted content
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+        SMIMEEnvelopedWriter.Builder envBldr = new SMIMEEnvelopedWriter.Builder();
+
+        //specify encryption certificate
+        envBldr.addRecipientInfoGenerator(new JceKeyTransRecipientInfoGenerator(_reciCert).setProvider(BC));
+
+        SMIMEEnvelopedWriter envWrt = envBldr.build(bOut, new JceCMSContentEncryptorBuilder(CMSAlgorithm.AES128_CBC).setProvider(BC).build());
+
+        OutputStream envOut = envWrt.getContentStream();
+
+        SMIMESignedWriter.Builder sigBldr = new SMIMESignedWriter.Builder();
+
+        //specify signature certificate
+        sigBldr.addCertificate(new JcaX509CertificateHolder(_signCert));
+
+        sigBldr.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider(BC).build("SHA256withRSA", _signKP.getPrivate(), _signCert));
+
+        //add the encryption stream to the signature stream
+        SMIMESignedWriter sigWrt = sigBldr.build(envOut);
+
+        OutputStream sigOut = sigWrt.getContentStream();
+
+        sigOut.write(simpleMessage);
+        
+        //sign file using sender private key
+        sigOut.close();
+        
+        //write full message to the byte array output stream before actually closing the SMIME Enveloped Writer (before this, bOut contains only the headers?)
+        envOut.close();
+
+        bOut.close();
+        
+        //
+        // parse / decrypt and compare to original file 
+        //
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(bOut.toByteArray()));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void envelopedData(MimeParserContext parserContext, Headers headers, OriginatorInformation originator, RecipientInformationStore recipients)
+                throws IOException, CMSException
+            {
+                RecipientInformation recipInfo = recipients.get(new JceKeyTransRecipientId(_reciCert));
+
+                assertNotNull(recipInfo);
+                
+                //decrypt the file using the receiver's private key before verifying signature
+                CMSTypedStream content = recipInfo.getContentStream(new JceKeyTransEnvelopedRecipient(_reciKP.getPrivate()));
+
+                MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+                
+                MimeParser p = provider.createParser(content.getContentStream());
+
+                p.parse(new SMimeParserListener()
+                {
+                    public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                        throws IOException
+                    {
+                        byte[] content = Streams.readAll(inputStream);
+
+                        assertTrue(org.bouncycastle.util.Arrays.areEqual(simpleMessageContent, content));
+                    }
+
+                    public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                        throws IOException, CMSException
+                    {
+                        SignerInformation signerInfo = signers.get(new JcaSignerId(_signCert));
+
+                        assertNotNull(signerInfo);
+
+                        Collection certCollection = certificates.getMatches(signerInfo.getSID());
+
+                        Iterator certIt = certCollection.iterator();
+                        X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                        try
+                        {
+                            assertEquals(true, signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                        }
+                        catch (OperatorCreationException e)
+                        {
+                            throw new CMSException(e.getMessage(), e);
+                        }
+                        catch (CertificateException e)
+                        {
+                            throw new CMSException(e.getMessage(), e);
+                        }
+
+                        dataParsed.markDone();
+                    }
+                });
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESigned.java b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESigned.java
new file mode 100644
index 0000000..4851945
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/mime/test/TestSMIMESigned.java
@@ -0,0 +1,192 @@
+package org.bouncycastle.mime.test;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.KeyPair;
+import java.security.Security;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collection;
+import java.util.Iterator;
+
+import junit.framework.TestCase;
+import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
+import org.bouncycastle.cms.SignerInformation;
+import org.bouncycastle.cms.SignerInformationStore;
+import org.bouncycastle.cms.jcajce.JcaSignerId;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoGeneratorBuilder;
+import org.bouncycastle.cms.jcajce.JcaSimpleSignerInfoVerifierBuilder;
+import org.bouncycastle.cms.test.CMSTestUtil;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.bouncycastle.mime.Headers;
+import org.bouncycastle.mime.MimeParser;
+import org.bouncycastle.mime.MimeParserContext;
+import org.bouncycastle.mime.MimeParserProvider;
+import org.bouncycastle.mime.smime.SMIMESignedWriter;
+import org.bouncycastle.mime.smime.SMimeParserListener;
+import org.bouncycastle.mime.smime.SMimeParserProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.operator.bc.BcDigestCalculatorProvider;
+import org.bouncycastle.util.Store;
+import org.bouncycastle.util.Strings;
+import org.bouncycastle.util.encoders.Base64;
+import org.bouncycastle.util.io.Streams;
+
+public class TestSMIMESigned
+    extends TestCase
+{
+    private static final String BC = BouncyCastleProvider.PROVIDER_NAME;
+
+    private static String _signDN;
+    private static KeyPair _signKP;
+
+    private static String _reciDN;
+    private static KeyPair _reciKP;
+
+    private static X509Certificate _signCert;
+    private static X509Certificate _reciCert;
+
+    private static boolean _initialised = false;
+
+    private static final byte[] simpleMessage = Strings.toByteArray(
+        "Content-Type: text/plain; name=null; charset=us-ascii\r\n" +
+            "Content-Transfer-Encoding: 7bit\r\n" +
+            "Content-Disposition: inline; filename=null\r\n" +
+            "\r\n" +
+            "Hello, world!\r\n");
+
+    private static final byte[] simpleMessageContent = Strings.toByteArray(
+            "Hello, world!\r\n");
+
+    private static final byte[] testMultipartMessage = Base64.decode(
+        "TUlNRS1WZXJzaW9uOiAxLjANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L21peGVkOyANCglib3VuZGFye" +
+            "T0iLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyIg0KQ29udGVudC1MYW5ndWFnZTogZW" +
+            "4NCkNvbnRlbnQtRGVzY3JpcHRpb246IEEgbWFpbCBmb2xsb3dpbmcgdGhlIERJUkVDVCBwcm9qZWN0IHN" +
+            "wZWNpZmljYXRpb25zDQoNCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzUwMTMyDQpDb250" +
+            "ZW50LVR5cGU6IHRleHQvcGxhaW47IG5hbWU9bnVsbDsgY2hhcnNldD11cy1hc2NpaQ0KQ29udGVudC1Uc" +
+            "mFuc2Zlci1FbmNvZGluZzogN2JpdA0KQ29udGVudC1EaXNwb3NpdGlvbjogaW5saW5lOyBmaWxlbmFtZT" +
+            "1udWxsDQoNCkNpYW8gZnJvbSB2aWVubmENCi0tLS0tLT1fUGFydF8wXzI2MDM5NjM4Ni4xMzUyOTA0NzU" +
+            "wMTMyLS0NCg==");
+
+    private static final byte[] testMultipartMessageContent = Base64.decode(
+        "LS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzINCkNvbnRlbnQtVHlwZTogdGV4dC9w" +
+            "bGFpbjsgbmFtZT1udWxsOyBjaGFyc2V0PXVzLWFzY2lpDQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5n" +
+            "OiA3Yml0DQpDb250ZW50LURpc3Bvc2l0aW9uOiBpbmxpbmU7IGZpbGVuYW1lPW51bGwNCg0KQ2lhbyBm" +
+            "cm9tIHZpZW5uYQ0KLS0tLS0tPV9QYXJ0XzBfMjYwMzk2Mzg2LjEzNTI5MDQ3NTAxMzItLQ0K");
+
+    private static void init()
+        throws Exception
+    {
+        if (!_initialised)
+        {
+            if (Security.getProvider("BC") == null)
+            {
+                Security.addProvider(new BouncyCastleProvider());
+            }
+
+            _initialised = true;
+
+            _signDN = "O=Bouncy Castle, C=AU";
+            _signKP = CMSTestUtil.makeKeyPair();
+            _signCert = CMSTestUtil.makeCertificate(_signKP, _signDN, _signKP, _signDN);
+
+            _reciDN = "CN=Doug, OU=Sales, O=Bouncy Castle, C=AU";
+            _reciKP = CMSTestUtil.makeKeyPair();
+            _reciCert = CMSTestUtil.makeCertificate(_reciKP, _reciDN, _signKP, _signDN);
+        }
+    }
+
+    public void setUp()
+        throws Exception
+    {
+        init();
+    }
+
+    public void testSimpleGeneration()
+        throws Exception
+    {
+        generationTest(simpleMessage, simpleMessageContent);
+    }
+
+    public void testEmbeddedMultipartGeneration()
+        throws Exception
+    {
+        generationTest(testMultipartMessage, testMultipartMessageContent);
+    }
+
+    private void generationTest(byte[] message, final byte[] messageContent)
+        throws Exception
+    {
+        //
+        // output
+        //
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        SMIMESignedWriter.Builder sigBldr = new SMIMESignedWriter.Builder();
+
+        sigBldr.addCertificate(new JcaX509CertificateHolder(_signCert));
+        
+        sigBldr.addSignerInfoGenerator(new JcaSimpleSignerInfoGeneratorBuilder().setProvider(BC).build("SHA256withRSA", _signKP.getPrivate(), _signCert));
+
+        SMIMESignedWriter sigWrt = sigBldr.build(bOut);
+
+        OutputStream out = sigWrt.getContentStream();
+
+        out.write(message);
+
+        out.close();
+        
+        //
+        // parse
+        //
+        final TestDoneFlag dataParsed = new TestDoneFlag();
+
+        MimeParserProvider provider = new SMimeParserProvider("7bit", new BcDigestCalculatorProvider());
+
+        MimeParser p = provider.createParser(new ReadOnceInputStream(bOut.toByteArray()));
+
+        p.parse(new SMimeParserListener()
+        {
+            public void content(MimeParserContext parserContext, Headers headers, InputStream inputStream)
+                throws IOException
+            {
+                byte[] content = Streams.readAll(inputStream);
+
+                assertTrue(org.bouncycastle.util.Arrays.areEqual(messageContent, content));
+            }
+
+            public void signedData(MimeParserContext parserContext, Headers headers, Store certificates, Store CRLs, Store attributeCertificates, SignerInformationStore signers)
+                throws IOException, CMSException
+            {
+                SignerInformation signerInfo = signers.get(new JcaSignerId(_signCert));
+
+                assertNotNull(signerInfo);
+
+                Collection certCollection = certificates.getMatches(signerInfo.getSID());
+
+                Iterator certIt = certCollection.iterator();
+                X509CertificateHolder certHolder = (X509CertificateHolder)certIt.next();
+
+                try
+                {
+                    assertEquals(true, signerInfo.verify(new JcaSimpleSignerInfoVerifierBuilder().setProvider("BC").build(certHolder)));
+                }
+                catch (OperatorCreationException e)
+                {
+                    throw new CMSException(e.getMessage(), e);
+                }
+                catch (CertificateException e)
+                {
+                    throw new CMSException(e.getMessage(), e);
+                }
+
+                dataParsed.markDone();
+            }
+        });
+
+        assertTrue(dataParsed.isDone());
+    }
+}
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java
index e1f9c35..c0c867d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/PEMParser.java
@@ -79,6 +79,13 @@
         parsers.put("PRIVATE KEY", new PrivateKeyParser());
     }
 
+    /**
+     * Read the next PEM object attempting to interpret the header and
+     * create a higher level object from the content.
+     *
+     * @return the next object in the stream, null if no objects left.
+     * @throws IOException in case of a parse error.
+     */
     public Object readObject()
         throws IOException
     {
@@ -229,9 +236,17 @@
                 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());
 
-                return new PEMKeyPair(pubInfo, privInfo);
+                if (pKey.getPublicKey() != null)
+                {
+                    SubjectPublicKeyInfo pubInfo = new SubjectPublicKeyInfo(algId, pKey.getPublicKey().getBytes());
+
+                    return new PEMKeyPair(pubInfo, privInfo);
+                }
+                else
+                {
+                    return new PEMKeyPair(null, privInfo);
+                }
             }
             catch (IOException e)
             {
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java b/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
index f822cba..cbabdf6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/PKCS8Generator.java
@@ -5,10 +5,13 @@
 import java.io.OutputStream;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.DERNull;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 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.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.operator.OutputEncryptor;
 import org.bouncycastle.util.io.pem.PemGenerationException;
 import org.bouncycastle.util.io.pem.PemObject;
@@ -30,6 +33,18 @@
     public static final ASN1ObjectIdentifier PBE_SHA1_RC2_128 = PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC;
     public static final ASN1ObjectIdentifier PBE_SHA1_RC2_40 = PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC;
 
+    public static final AlgorithmIdentifier PRF_HMACSHA1 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA224 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA224, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA256 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA256, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA384 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA384, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA512 = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA512, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACGOST3411 = new AlgorithmIdentifier(CryptoProObjectIdentifiers.gostR3411Hmac, DERNull.INSTANCE);
+
+    public static final AlgorithmIdentifier PRF_HMACSHA3_224 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_224, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA3_256 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_256, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA3_384 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_384, DERNull.INSTANCE);
+    public static final AlgorithmIdentifier PRF_HMACSHA3_512 = new AlgorithmIdentifier(NISTObjectIdentifiers.id_hmacWithSHA3_512, DERNull.INSTANCE);
+
     private PrivateKeyInfo key;
     private OutputEncryptor outputEncryptor;
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
index 709af31..386e74c 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/X509TrustedCertificateBlock.java
@@ -3,6 +3,7 @@
 import java.io.IOException;
 
 import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Object;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.util.Arrays;
 
@@ -26,7 +27,17 @@
         ASN1InputStream aIn = new ASN1InputStream(encoding);
 
         this.certificateHolder = new X509CertificateHolder(aIn.readObject().getEncoded());
-        this.trustBlock = new CertificateTrustBlock(aIn.readObject().getEncoded());
+
+        ASN1Object tBlock = aIn.readObject();
+
+        if (tBlock != null)
+        {
+            this.trustBlock = new CertificateTrustBlock(tBlock.getEncoded());
+        }
+        else
+        {
+            this.trustBlock = null;
+        }
     }
 
     public byte[] getEncoded()
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
index c2877d2..762719d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8DecryptorProviderBuilder.java
@@ -30,9 +30,12 @@
 import org.bouncycastle.operator.OperatorCreationException;
 import org.bouncycastle.util.Strings;
 
+/**
+ * DecryptorProviderBuilder for producing DecryptorProvider for use with PKCS8EncryptedPrivateKeyInfo.
+ */
 public class JceOpenSSLPKCS8DecryptorProviderBuilder
 {
-    private JcaJceHelper helper = new DefaultJcaJceHelper();
+    private JcaJceHelper helper;
 
     public JceOpenSSLPKCS8DecryptorProviderBuilder()
     {
@@ -77,8 +80,17 @@
 
                         String oid = scheme.getAlgorithm().getId();
 
-                        SecretKey key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, oid, password, salt, iterationCount);
+                        SecretKey key;
 
+                        if (PEMUtilities.isHmacSHA1(defParams.getPrf()))
+                        {
+                            key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, oid, password, salt, iterationCount);
+                        }
+                        else
+                        {
+                            key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, oid, password, salt, iterationCount, defParams.getPrf());
+                        }
+                        
                         cipher = helper.createCipher(oid);
                         AlgorithmParameters algParams = helper.createAlgorithmParameters(oid);
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
index 95d0883..612b531 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/JceOpenSSLPKCS8EncryptorBuilder.java
@@ -16,9 +16,11 @@
 import org.bouncycastle.asn1.ASN1Integer;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
 import org.bouncycastle.asn1.DERSequence;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.EncryptionScheme;
 import org.bouncycastle.asn1.pkcs.KeyDerivationFunc;
 import org.bouncycastle.asn1.pkcs.PBES2Parameters;
 import org.bouncycastle.asn1.pkcs.PBKDF2Params;
@@ -62,6 +64,7 @@
     private char[] password;
 
     private SecretKey key;
+    private AlgorithmIdentifier prf = new AlgorithmIdentifier(PKCSObjectIdentifiers.id_hmacWithSHA1, DERNull.INSTANCE);
 
     public JceOpenSSLPKCS8EncryptorBuilder(ASN1ObjectIdentifier algorithm)
     {
@@ -84,6 +87,20 @@
         return this;
     }
 
+    /**
+     * Set the PRF to use for key generation. By default this is HmacSHA1.
+     *
+     * @param prf algorithm id for PRF.
+     *
+     * @return the current builder.
+     */
+    public JceOpenSSLPKCS8EncryptorBuilder setPRF(AlgorithmIdentifier prf)
+    {
+        this.prf = prf;
+
+        return this;
+    }
+
     public JceOpenSSLPKCS8EncryptorBuilder setIterationCount(int iterationCount)
     {
         this.iterationCount = iterationCount;
@@ -110,15 +127,11 @@
     {
         final AlgorithmIdentifier algID;
 
-        salt = new byte[20];
-
         if (random == null)
         {
             random = new SecureRandom();
         }
 
-        random.nextBytes(salt);
-
         try
         {
             this.cipher = helper.createCipher(algOID.getId());
@@ -135,12 +148,16 @@
 
         if (PEMUtilities.isPKCS5Scheme2(algOID))
         {
+            salt = new byte[PEMUtilities.getSaltSize(prf.getAlgorithm())];
+
+            random.nextBytes(salt);
+
             params = paramGen.generateParameters();
 
             try
             {
-                KeyDerivationFunc scheme = new KeyDerivationFunc(algOID, ASN1Primitive.fromByteArray(params.getEncoded()));
-                KeyDerivationFunc func = new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount));
+                EncryptionScheme scheme = new EncryptionScheme(algOID, ASN1Primitive.fromByteArray(params.getEncoded()));
+                KeyDerivationFunc func = new KeyDerivationFunc(PKCSObjectIdentifiers.id_PBKDF2, new PBKDF2Params(salt, iterationCount, prf));
 
                 ASN1EncodableVector v = new ASN1EncodableVector();
 
@@ -156,7 +173,14 @@
 
             try
             {
-                key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, algOID.getId(), password, salt, iterationCount);
+                if (PEMUtilities.isHmacSHA1(prf))
+                {
+                    key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, algOID.getId(), password, salt, iterationCount);
+                }
+                else
+                {
+                    key = PEMUtilities.generateSecretKeyForPKCS5Scheme2(helper, algOID.getId(), password, salt, iterationCount, prf);
+                }
 
                 cipher.init(Cipher.ENCRYPT_MODE, key, params);
             }
@@ -169,6 +193,10 @@
         {
             ASN1EncodableVector v = new ASN1EncodableVector();
 
+            salt = new byte[20];
+
+            random.nextBytes(salt);
+
             v.add(new DEROctetString(salt));
             v.add(new ASN1Integer(iterationCount));
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java
index 83d2098..7a97804 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/jcajce/PEMUtilities.java
@@ -20,8 +20,10 @@
 import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.openssl.EncryptionException;
 import org.bouncycastle.openssl.PEMException;
@@ -32,6 +34,8 @@
     private static final Map KEYSIZES = new HashMap();
     private static final Set PKCS5_SCHEME_1 = new HashSet();
     private static final Set PKCS5_SCHEME_2 = new HashSet();
+    private static final Map PRFS = new HashMap();
+    private static final Map PRFS_SALT = new HashMap();
 
     static
     {
@@ -58,6 +62,28 @@
         KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd3_KeyTripleDES_CBC, Integers.valueOf(192));
         KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd128BitRC2_CBC, Integers.valueOf(128));
         KEYSIZES.put(PKCSObjectIdentifiers.pbeWithSHAAnd40BitRC2_CBC, Integers.valueOf(40));
+
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA1, "PBKDF2withHMACSHA1");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA256, "PBKDF2withHMACSHA256");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA512, "PBKDF2withHMACSHA512");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA224, "PBKDF2withHMACSHA224");
+        PRFS.put(PKCSObjectIdentifiers.id_hmacWithSHA384, "PBKDF2withHMACSHA384");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, "PBKDF2withHMACSHA3-224");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, "PBKDF2withHMACSHA3-256");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, "PBKDF2withHMACSHA3-384");
+        PRFS.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, "PBKDF2withHMACSHA3-512");
+        PRFS.put(CryptoProObjectIdentifiers.gostR3411Hmac, "PBKDF2withHMACGOST3411");
+
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(20));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(32));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(64));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(28));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(28));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(32));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(64));
+        PRFS_SALT.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(32));
     }
 
     static int getKeySize(String algorithm)
@@ -70,6 +96,21 @@
         return ((Integer)KEYSIZES.get(algorithm)).intValue();
     }
 
+    static int getSaltSize(ASN1ObjectIdentifier algorithm)
+    {
+        if (!PRFS_SALT.containsKey(algorithm))
+        {
+            throw new IllegalStateException("no salt size for algorithm: " + algorithm);
+        }
+
+        return ((Integer)PRFS_SALT.get(algorithm)).intValue();
+    }
+
+    static boolean isHmacSHA1(AlgorithmIdentifier prf)
+    {
+        return prf == null || prf.getAlgorithm().equals(PKCSObjectIdentifiers.id_hmacWithSHA1);
+    }
+
     static boolean isPKCS5Scheme1(ASN1ObjectIdentifier algOid)
     {
         return PKCS5_SCHEME_1.contains(algOid);
@@ -89,6 +130,22 @@
         throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException
     {
         SecretKeyFactory keyGen = helper.createSecretKeyFactory("PBKDF2with8BIT");
+                            
+        SecretKey sKey = keyGen.generateSecret(new PBEKeySpec(password, salt, iterationCount, PEMUtilities.getKeySize(algorithm)));
+
+        return new SecretKeySpec(sKey.getEncoded(), algorithm);
+    }
+
+    public static SecretKey generateSecretKeyForPKCS5Scheme2(JcaJceHelper helper, String algorithm, char[] password, byte[] salt, int iterationCount, AlgorithmIdentifier prf)
+        throws NoSuchProviderException, NoSuchAlgorithmException, InvalidKeySpecException
+    {
+        String prfName = (String)PRFS.get(prf.getAlgorithm());
+        if (prfName == null)
+        {
+            throw new NoSuchAlgorithmException("unknown PRF in PKCS#2: " + prf.getAlgorithm());
+        }
+
+        SecretKeyFactory keyGen = helper.createSecretKeyFactory(prfName);
 
         SecretKey sKey = keyGen.generateSecret(new PBEKeySpec(password, salt, iterationCount, PEMUtilities.getKeySize(algorithm)));
 
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 b7b25c3..1534827 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/test/AllTests.java
@@ -17,6 +17,7 @@
 import junit.framework.TestSuite;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
 import org.bouncycastle.openssl.PEMParser;
 import org.bouncycastle.openssl.PKCS8Generator;
@@ -85,6 +86,17 @@
         encryptedTestNew(key, PKCS8Generator.AES_256_CBC);
         encryptedTestNew(key, PKCS8Generator.DES3_CBC);
         encryptedTestNew(key, PKCS8Generator.PBE_SHA1_3DES);
+
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA1);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA224);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA256);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA384);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA512);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_224);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_256);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_384);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACSHA3_512);
+        encryptedTestNew(key, PKCS8Generator.AES_256_CBC, PKCS8Generator.PRF_HMACGOST3411);
     }
 
     private void encryptedTestNew(PrivateKey key, ASN1ObjectIdentifier algorithm)
@@ -114,6 +126,34 @@
         assertEquals(key, rdKey);
     }
 
+    private void encryptedTestNew(PrivateKey key, ASN1ObjectIdentifier algorithm, AlgorithmIdentifier prf)
+        throws NoSuchProviderException, NoSuchAlgorithmException, IOException, OperatorCreationException, PKCSException
+    {
+        ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+        JcaPEMWriter pWrt = new JcaPEMWriter(new OutputStreamWriter(bOut));
+
+        JceOpenSSLPKCS8EncryptorBuilder encryptorBuilder = new JceOpenSSLPKCS8EncryptorBuilder(algorithm);
+
+        encryptorBuilder.setProvider("BC");
+        encryptorBuilder.setPasssword("hello".toCharArray());
+        encryptorBuilder.setPRF(prf);
+
+        PKCS8Generator pkcs8 = new JcaPKCS8Generator(key, encryptorBuilder.build());
+
+        pWrt.writeObject(pkcs8);
+
+        pWrt.close();
+
+        PEMParser pRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
+
+        PKCS8EncryptedPrivateKeyInfo pInfo = (PKCS8EncryptedPrivateKeyInfo)pRd.readObject();
+
+        PrivateKey rdKey = new JcaPEMKeyConverter().setProvider("BC").getPrivateKey(pInfo.decryptPrivateKeyInfo(new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC").build("hello".toCharArray())));
+
+
+        assertEquals(key, rdKey);
+    }
+
     public void testVectors()
         throws Exception
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java b/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java
index 338fe7d..0b816ae 100644
--- a/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java
+++ b/bcpkix/src/main/java/org/bouncycastle/openssl/test/ParserTest.java
@@ -58,6 +58,7 @@
         return "PEMParserTest";
     }
 
+
     private PEMParser openPEMResource(
         String          fileName)
     {
@@ -267,6 +268,7 @@
         doDudPasswordTest("aaf9c4d",17, "corrupted stream - out of bounds length found");
 
         doNoPasswordTest();
+        doNoECPublicKeyTest();
 
         // encrypted private key test
         InputDecryptorProvider pkcs8Prov = new JceOpenSSLPKCS8DecryptorProviderBuilder().setProvider("BC").build("password".toCharArray());
@@ -323,6 +325,8 @@
         trusted = (X509TrustedCertificateBlock)pemRd.readObject();
 
         checkTrustedCert(trusted);
+
+
     }
 
     private void checkTrustedCert(X509TrustedCertificateBlock trusted)
@@ -355,15 +359,15 @@
 
     private void keyPairTest(
         String   name,
-        KeyPair pair) 
+        KeyPair pair)
         throws IOException
     {
         PEMParser pemRd;
         ByteArrayOutputStream bOut = new ByteArrayOutputStream();
         JcaPEMWriter             pWrt = new JcaPEMWriter(new OutputStreamWriter(bOut));
-        
+
         pWrt.writeObject(pair.getPublic());
-        
+
         pWrt.close();
 
         pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
@@ -377,22 +381,22 @@
         {
             fail("Failed public key read: " + name);
         }
-        
+
         bOut = new ByteArrayOutputStream();
         pWrt = new JcaPEMWriter(new OutputStreamWriter(bOut));
-        
+
         pWrt.writeObject(pair.getPrivate());
-        
+
         pWrt.close();
-        
+
         pemRd = new PEMParser(new InputStreamReader(new ByteArrayInputStream(bOut.toByteArray())));
-        
+
         KeyPair kPair = converter.getKeyPair((PEMKeyPair)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);
@@ -532,6 +536,25 @@
         }
     }
 
+    private void doNoECPublicKeyTest()
+        throws Exception
+    {
+        // EC private key without the public key defined. Note: this encoding is actually invalid.
+        String ecSample =
+                    "-----BEGIN EC PRIVATE KEY-----\n" +
+                    "MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgvYiiubZYNO1WXXi3\n" +
+                    "jmGT9DLeFemvlmR1zTA0FdcSAG2gCgYIKoZIzj0DAQehRANCAATNXYa06ykwhxuy\n" +
+                    "Dg+q6zsVqOLk9LtXz/1fzf9AkAVm9lBMTZAh+FRfregBgl08LATztGlTh/z0dPnp\n" +
+                    "dW2jFrDn\n" +
+                    "-----END EC PRIVATE KEY-----";
+
+        PEMParser pemRd = new PEMParser(new StringReader(ecSample));
+
+        PEMKeyPair kp = (PEMKeyPair)pemRd.readObject();
+
+        isTrue(kp.getPublicKeyInfo() == null);
+    }
+
     public static void main(
         String[]    args)
     {
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
index fadef60..2274a19 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentSigner.java
@@ -4,8 +4,18 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * General interface for an operator that is able to create a signature from
+ * a stream of output.
+ */
 public interface ContentSigner
 {
+    /**
+     * Return the algorithm identifier describing the signature
+     * algorithm and parameters this signer generates.
+     *
+     * @return algorithm oid and parameters.
+     */
     AlgorithmIdentifier getAlgorithmIdentifier();
 
     /**
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
index 54d9ef1..1729a60 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/ContentVerifier.java
@@ -4,11 +4,15 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * General interface for an operator that is able to verify a signature based
+ * on data in a stream of output.
+ */
 public interface ContentVerifier
 {
     /**
      * Return the algorithm identifier describing the signature
-     * algorithm and parameters this expander supports.
+     * algorithm and parameters this verifier supports.
      *
      * @return algorithm oid and parameters.
      */
@@ -24,6 +28,9 @@
     OutputStream getOutputStream();
 
     /**
+     * Return true if the expected value of the signature matches the data passed
+     * into the stream.
+     *
      * @param expected expected value of the signature on the data.
      * @return true if the signature verifies, false otherwise
      */
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java
index 1f48c53..ad9f5b6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultAlgorithmNameFinder.java
@@ -14,6 +14,7 @@
 import org.bouncycastle.asn1.ntt.NTTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -37,19 +38,28 @@
         algorithms.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
         algorithms.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410-94");
         algorithms.put(CryptoProObjectIdentifiers.gostR3411, "GOST3411");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411WITHGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411WITHGOST3410-2012-512");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411WITHECGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411WITHECGOST3410-2012-512");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHGOST3410-2012-512");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
+        algorithms.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, "SHA1WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, "SHA224WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, "SHA256WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, "SHA384WITHCVC-ECDSA");
         algorithms.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, "SHA512WITHCVC-ECDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha384, "SHA384WITHDSA");
-        algorithms.put(NISTObjectIdentifiers.dsa_with_sha512, "SHA512WITHDSA");
+
         algorithms.put(NISTObjectIdentifiers.id_sha224, "SHA224");
         algorithms.put(NISTObjectIdentifiers.id_sha256, "SHA256");
         algorithms.put(NISTObjectIdentifiers.id_sha384, "SHA384");
         algorithms.put(NISTObjectIdentifiers.id_sha512, "SHA512");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_224, "SHA3-224");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_256, "SHA3-256");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_384, "SHA3-384");
+        algorithms.put(NISTObjectIdentifiers.id_sha3_512, "SHA3-512");
         algorithms.put(OIWObjectIdentifiers.elGamalAlgorithm, "ELGAMAL");
         algorithms.put(OIWObjectIdentifiers.idSHA1, "SHA1");
         algorithms.put(OIWObjectIdentifiers.md5WithRSA, "MD5WITHRSA");
@@ -65,6 +75,10 @@
         algorithms.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, "SHA256WITHRSA");
         algorithms.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, "SHA384WITHRSA");
         algorithms.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, "SHA3-224WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, "SHA3-256WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, "SHA3-384WITHRSA");
+        algorithms.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, "SHA3-512WITHRSA");
         algorithms.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         algorithms.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
         algorithms.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
@@ -77,7 +91,19 @@
         algorithms.put(X9ObjectIdentifiers.ecdsa_with_SHA256, "SHA256WITHECDSA");
         algorithms.put(X9ObjectIdentifiers.ecdsa_with_SHA384, "SHA384WITHECDSA");
         algorithms.put(X9ObjectIdentifiers.ecdsa_with_SHA512, "SHA512WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_224, "SHA3-224WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_256, "SHA3-256WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, "SHA3-384WITHECDSA");
+        algorithms.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_512, "SHA3-512WITHECDSA");
         algorithms.put(X9ObjectIdentifiers.id_dsa_with_sha1, "SHA1WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha384, "SHA384WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.dsa_with_sha512, "SHA512WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_224, "SHA3-224WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_256, "SHA3-256WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_384, "SHA3-384WITHDSA");
+        algorithms.put(NISTObjectIdentifiers.id_dsa_with_sha3_512, "SHA3-512WITHDSA");
         algorithms.put(GNUObjectIdentifiers.Tiger_192, "Tiger");
 
         algorithms.put(PKCSObjectIdentifiers.RC2_CBC, "RC2/CBC");
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
index fbe8797..98fdbad 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultDigestAlgorithmIdentifierFinder.java
@@ -8,10 +8,13 @@
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
 import org.bouncycastle.asn1.bsi.BSIObjectIdentifiers;
 import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.eac.EACObjectIdentifiers;
+import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -52,6 +55,13 @@
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, NISTObjectIdentifiers.id_sha256);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA384, NISTObjectIdentifiers.id_sha384);
         digestOids.put(BSIObjectIdentifiers.ecdsa_plain_SHA512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(BSIObjectIdentifiers.ecdsa_plain_RIPEMD160, TeleTrusTObjectIdentifiers.ripemd160);
+
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_1, OIWObjectIdentifiers.idSHA1);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_224, NISTObjectIdentifiers.id_sha224);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_256, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_384, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(EACObjectIdentifiers.id_TA_ECDSA_SHA_512, NISTObjectIdentifiers.id_sha512);
 
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
         digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
@@ -77,10 +87,14 @@
 
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
 
         digestOids.put(BCObjectIdentifiers.sphincs256_with_SHA3_512, NISTObjectIdentifiers.id_sha3_512);
         digestOids.put(BCObjectIdentifiers.sphincs256_with_SHA512, NISTObjectIdentifiers.id_sha512);
 
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
+        
         digestNameToOids.put("SHA-1", OIWObjectIdentifiers.idSHA1);
         digestNameToOids.put("SHA-224", NISTObjectIdentifiers.id_sha224);
         digestNameToOids.put("SHA-256", NISTObjectIdentifiers.id_sha256);
@@ -106,6 +120,8 @@
         digestNameToOids.put("SHAKE-256", NISTObjectIdentifiers.id_shake256);
 
         digestNameToOids.put("GOST3411", CryptoProObjectIdentifiers.gostR3411);
+        digestNameToOids.put("GOST3411-2012-256", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+        digestNameToOids.put("GOST3411-2012-512", RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
 
         digestNameToOids.put("MD2", PKCSObjectIdentifiers.md2);
         digestNameToOids.put("MD4", PKCSObjectIdentifiers.md4);
@@ -114,6 +130,8 @@
         digestNameToOids.put("RIPEMD128", TeleTrusTObjectIdentifiers.ripemd128);
         digestNameToOids.put("RIPEMD160", TeleTrusTObjectIdentifiers.ripemd160);
         digestNameToOids.put("RIPEMD256", TeleTrusTObjectIdentifiers.ripemd256);
+
+        digestNameToOids.put("SM3", GMObjectIdentifiers.sm3);
     }
 
     public AlgorithmIdentifier find(AlgorithmIdentifier sigAlgId)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
index 49e5027..9366086 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSecretKeySizeProvider.java
@@ -31,6 +31,9 @@
         keySizes.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, Integers.valueOf(192));
         keySizes.put(PKCSObjectIdentifiers.des_EDE3_CBC, Integers.valueOf(192));
 
+        keySizes.put(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC, Integers.valueOf(64));
+        keySizes.put(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC, Integers.valueOf(64));
+
         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));
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
index b428e0d..d81ecca 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DefaultSignatureAlgorithmIdentifierFinder.java
@@ -18,6 +18,7 @@
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
@@ -38,6 +39,8 @@
     private static final ASN1ObjectIdentifier ENCRYPTION_RSA_PSS = PKCSObjectIdentifiers.id_RSASSA_PSS;
     private static final ASN1ObjectIdentifier ENCRYPTION_GOST3410 = CryptoProObjectIdentifiers.gostR3410_94;
     private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410 = CryptoProObjectIdentifiers.gostR3410_2001;
+    private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410_2012_256 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256;
+    private static final ASN1ObjectIdentifier ENCRYPTION_ECGOST3410_2012_512 = RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512;
 
     static
     {
@@ -60,6 +63,10 @@
         algorithms.put("SHA256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("SHA512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-224WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-256WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-384WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
+        algorithms.put("SHA3-512WITHRSAANDMGF1", PKCSObjectIdentifiers.id_RSASSA_PSS);
         algorithms.put("RIPEMD160WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         algorithms.put("RIPEMD160WITHRSA", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         algorithms.put("RIPEMD128WITHRSAENCRYPTION", TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
@@ -72,6 +79,22 @@
         algorithms.put("SHA256WITHDSA", NISTObjectIdentifiers.dsa_with_sha256);
         algorithms.put("SHA384WITHDSA", NISTObjectIdentifiers.dsa_with_sha384);
         algorithms.put("SHA512WITHDSA", NISTObjectIdentifiers.dsa_with_sha512);
+        algorithms.put("SHA3-224WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_224);
+        algorithms.put("SHA3-256WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_256);
+        algorithms.put("SHA3-384WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_384);
+        algorithms.put("SHA3-512WITHDSA", NISTObjectIdentifiers.id_dsa_with_sha3_512);
+        algorithms.put("SHA3-224WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
+        algorithms.put("SHA3-256WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
+        algorithms.put("SHA3-384WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
+        algorithms.put("SHA3-512WITHECDSA", NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
+        algorithms.put("SHA3-224WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+        algorithms.put("SHA3-256WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+        algorithms.put("SHA3-384WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+        algorithms.put("SHA3-512WITHRSA", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
+        algorithms.put("SHA3-224WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+        algorithms.put("SHA3-256WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+        algorithms.put("SHA3-384WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+        algorithms.put("SHA3-512WITHRSAENCRYPTION", NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
         algorithms.put("SHA1WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA1);
         algorithms.put("ECDSAWITHSHA1", X9ObjectIdentifiers.ecdsa_with_SHA1);
         algorithms.put("SHA224WITHECDSA", X9ObjectIdentifiers.ecdsa_with_SHA224);
@@ -83,6 +106,14 @@
         algorithms.put("GOST3411WITHECGOST3410", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
         algorithms.put("GOST3411WITHECGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
         algorithms.put("GOST3411WITHGOST3410-2001", CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        algorithms.put("GOST3411WITHECGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+        algorithms.put("GOST3411WITHGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411WITHGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+        algorithms.put("GOST3411-2012-256WITHECGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411-2012-512WITHECGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
+        algorithms.put("GOST3411-2012-256WITHGOST3410-2012-256", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        algorithms.put("GOST3411-2012-512WITHGOST3410-2012-512", RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
         algorithms.put("SHA1WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA1);
         algorithms.put("SHA224WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA224);
         algorithms.put("SHA256WITHPLAIN-ECDSA", BSIObjectIdentifiers.ecdsa_plain_SHA256);
@@ -97,6 +128,43 @@
         algorithms.put("SHA3-512WITHSPHINCS256", BCObjectIdentifiers.sphincs256_with_SHA3_512);
         algorithms.put("SHA512WITHSPHINCS256", BCObjectIdentifiers.sphincs256_with_SHA512);
         algorithms.put("SM3WITHSM2", GMObjectIdentifiers.sm2sign_with_sm3);
+
+        algorithms.put("SHA256WITHXMSS", BCObjectIdentifiers.xmss_SHA256ph);
+        algorithms.put("SHA512WITHXMSS", BCObjectIdentifiers.xmss_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSS", BCObjectIdentifiers.xmss_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSS", BCObjectIdentifiers.xmss_SHAKE256ph);
+
+        algorithms.put("SHA256WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA256ph);
+        algorithms.put("SHA512WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSSMT", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+
+        algorithms.put("SHA256WITHXMSS-SHA256", BCObjectIdentifiers.xmss_SHA256ph);
+        algorithms.put("SHA512WITHXMSS-SHA512", BCObjectIdentifiers.xmss_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSS-SHAKE128", BCObjectIdentifiers.xmss_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSS-SHAKE256", BCObjectIdentifiers.xmss_SHAKE256ph);
+
+        algorithms.put("SHA256WITHXMSSMT-SHA256", BCObjectIdentifiers.xmss_mt_SHA256ph);
+        algorithms.put("SHA512WITHXMSSMT-SHA512", BCObjectIdentifiers.xmss_mt_SHA512ph);
+        algorithms.put("SHAKE128WITHXMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        algorithms.put("SHAKE256WITHXMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+
+        algorithms.put("XMSS-SHA256", BCObjectIdentifiers.xmss_SHA256);
+        algorithms.put("XMSS-SHA512", BCObjectIdentifiers.xmss_SHA512);
+        algorithms.put("XMSS-SHAKE128", BCObjectIdentifiers.xmss_SHAKE128);
+        algorithms.put("XMSS-SHAKE256", BCObjectIdentifiers.xmss_SHAKE256);
+
+        algorithms.put("XMSSMT-SHA256", BCObjectIdentifiers.xmss_mt_SHA256);
+        algorithms.put("XMSSMT-SHA512", BCObjectIdentifiers.xmss_mt_SHA512);
+        algorithms.put("XMSSMT-SHAKE128", BCObjectIdentifiers.xmss_mt_SHAKE128);
+        algorithms.put("XMSSMT-SHAKE256", BCObjectIdentifiers.xmss_mt_SHAKE256);
+
+        algorithms.put("QTESLA-I", BCObjectIdentifiers.qTESLA_I);
+        algorithms.put("QTESLA-III-SIZE", BCObjectIdentifiers.qTESLA_III_size);
+        algorithms.put("QTESLA-III-SPEED", BCObjectIdentifiers.qTESLA_III_speed);
+        algorithms.put("QTESLA-P-I", BCObjectIdentifiers.qTESLA_p_I);
+        algorithms.put("QTESLA-P-III", BCObjectIdentifiers.qTESLA_p_III);
+
         //
         // According to RFC 3279, the ASN.1 encoding SHALL (id-dsa-with-sha1) or MUST (ecdsa-with-SHA*) omit the parameters field.
         // The parameters field SHALL be NULL for RSA based signature algorithms.
@@ -111,12 +179,22 @@
         noParams.add(NISTObjectIdentifiers.dsa_with_sha256);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha384);
         noParams.add(NISTObjectIdentifiers.dsa_with_sha512);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_dsa_with_sha3_512);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_224);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_256);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_384);
+        noParams.add(NISTObjectIdentifiers.id_ecdsa_with_sha3_512);
 
         //
         // RFC 4491
         //
         noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94);
         noParams.add(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001);
+        noParams.add(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256);
+        noParams.add(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512);
 
         //
         // SPHINCS-256
@@ -125,6 +203,36 @@
         noParams.add(BCObjectIdentifiers.sphincs256_with_SHA3_512);
 
         //
+        // XMSS
+        //
+        noParams.add(BCObjectIdentifiers.xmss_SHA256ph);
+        noParams.add(BCObjectIdentifiers.xmss_SHA512ph);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE128ph);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE256ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA256ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA512ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128ph);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256ph);
+
+        noParams.add(BCObjectIdentifiers.xmss_SHA256);
+        noParams.add(BCObjectIdentifiers.xmss_SHA512);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE128);
+        noParams.add(BCObjectIdentifiers.xmss_SHAKE256);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA256);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHA512);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE128);
+        noParams.add(BCObjectIdentifiers.xmss_mt_SHAKE256);
+
+        //
+        // qTESLA
+        //
+        noParams.add(BCObjectIdentifiers.qTESLA_I);
+        noParams.add(BCObjectIdentifiers.qTESLA_III_size);
+        noParams.add(BCObjectIdentifiers.qTESLA_III_speed);
+        noParams.add(BCObjectIdentifiers.qTESLA_p_I);
+        noParams.add(BCObjectIdentifiers.qTESLA_p_III);
+
+        //
         // SM2
         //
         noParams.add(GMObjectIdentifiers.sm2sign_with_sm3);
@@ -140,6 +248,10 @@
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd128);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd160);
         pkcs15RsaEncryption.add(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384);
+        pkcs15RsaEncryption.add(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512);
 
         //
         // explicit params
@@ -159,6 +271,18 @@
         AlgorithmIdentifier sha512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha512, DERNull.INSTANCE);
         params.put("SHA512WITHRSAANDMGF1", createPSSParams(sha512AlgId, 64));
 
+        AlgorithmIdentifier sha3_224AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_224, DERNull.INSTANCE);
+        params.put("SHA3-224WITHRSAANDMGF1", createPSSParams(sha3_224AlgId, 28));
+
+        AlgorithmIdentifier sha3_256AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_256, DERNull.INSTANCE);
+        params.put("SHA3-256WITHRSAANDMGF1", createPSSParams(sha3_256AlgId, 32));
+
+        AlgorithmIdentifier sha3_384AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_384, DERNull.INSTANCE);
+        params.put("SHA3-384WITHRSAANDMGF1", createPSSParams(sha3_384AlgId, 48));
+
+        AlgorithmIdentifier sha3_512AlgId = new AlgorithmIdentifier(NISTObjectIdentifiers.id_sha3_512, DERNull.INSTANCE);
+        params.put("SHA3-512WITHRSAANDMGF1", createPSSParams(sha3_512AlgId, 64));
+
         //
         // digests
         //
@@ -166,6 +290,23 @@
         digestOids.put(PKCSObjectIdentifiers.sha256WithRSAEncryption, NISTObjectIdentifiers.id_sha256);
         digestOids.put(PKCSObjectIdentifiers.sha384WithRSAEncryption, NISTObjectIdentifiers.id_sha384);
         digestOids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha224, NISTObjectIdentifiers.id_sha224);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha256, NISTObjectIdentifiers.id_sha256);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha384, NISTObjectIdentifiers.id_sha384);
+        digestOids.put(NISTObjectIdentifiers.dsa_with_sha512, NISTObjectIdentifiers.id_sha512);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(NISTObjectIdentifiers.id_dsa_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(NISTObjectIdentifiers.id_ecdsa_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_224, NISTObjectIdentifiers.id_sha3_224);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_256, NISTObjectIdentifiers.id_sha3_256);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_384, NISTObjectIdentifiers.id_sha3_384);
+        digestOids.put(NISTObjectIdentifiers.id_rsassa_pkcs1_v1_5_with_sha3_512, NISTObjectIdentifiers.id_sha3_512);
+
         digestOids.put(PKCSObjectIdentifiers.md2WithRSAEncryption, PKCSObjectIdentifiers.md2);
         digestOids.put(PKCSObjectIdentifiers.md4WithRSAEncryption, PKCSObjectIdentifiers.md4);
         digestOids.put(PKCSObjectIdentifiers.md5WithRSAEncryption, PKCSObjectIdentifiers.md5);
@@ -175,13 +316,14 @@
         digestOids.put(TeleTrusTObjectIdentifiers.rsaSignatureWithripemd256, TeleTrusTObjectIdentifiers.ripemd256);
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, CryptoProObjectIdentifiers.gostR3411);
         digestOids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, CryptoProObjectIdentifiers.gostR3411);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256);
+        digestOids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512);
+        digestOids.put(GMObjectIdentifiers.sm2sign_with_sm3, GMObjectIdentifiers.sm3);
     }
 
     private static AlgorithmIdentifier generate(String signatureAlgorithm)
     {
         AlgorithmIdentifier sigAlgId;
-        AlgorithmIdentifier encAlgId;
-        AlgorithmIdentifier digAlgId;
 
         String algorithmName = Strings.toUpperCase(signatureAlgorithm);
         ASN1ObjectIdentifier sigOID = (ASN1ObjectIdentifier)algorithms.get(algorithmName);
@@ -203,24 +345,6 @@
             sigAlgId = new AlgorithmIdentifier(sigOID, DERNull.INSTANCE);
         }
 
-        if (pkcs15RsaEncryption.contains(sigOID))
-        {
-            encAlgId = new AlgorithmIdentifier(PKCSObjectIdentifiers.rsaEncryption, DERNull.INSTANCE);
-        }
-        else
-        {
-            encAlgId = sigAlgId;
-        }
-
-        if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
-        {
-            digAlgId = ((RSASSAPSSparams)sigAlgId.getParameters()).getHashAlgorithm();
-        }
-        else
-        {
-            digAlgId = new AlgorithmIdentifier((ASN1ObjectIdentifier)digestOids.get(sigOID), DERNull.INSTANCE);
-        }
-
         return sigAlgId;
     }
 
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
index 2365270..d912f00 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/DigestCalculatorProvider.java
@@ -2,6 +2,9 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * The base interface for a provider of DigestCalculator implementations.
+ */
 public interface DigestCalculatorProvider
 {
     DigestCalculator get(AlgorithmIdentifier digestAlgorithmIdentifier)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java b/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java
index 0572afc..45144cc 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/MacCalculator.java
@@ -4,6 +4,10 @@
 
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 
+/**
+ * General interface for a key initialized operator that is able to calculate a MAC from
+ * a stream of output.
+ */
 public interface MacCalculator
 {
     AlgorithmIdentifier getAlgorithmIdentifier();
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
index ea563d5..3634f99 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/bc/BcDefaultDigestProvider.java
@@ -8,22 +8,11 @@
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.crypto.ExtendedDigest;
-import org.bouncycastle.crypto.digests.GOST3411Digest;
-import org.bouncycastle.crypto.digests.MD2Digest;
-import org.bouncycastle.crypto.digests.MD4Digest;
-import org.bouncycastle.crypto.digests.MD5Digest;
-import org.bouncycastle.crypto.digests.RIPEMD128Digest;
-import org.bouncycastle.crypto.digests.RIPEMD160Digest;
-import org.bouncycastle.crypto.digests.RIPEMD256Digest;
-import org.bouncycastle.crypto.digests.SHA1Digest;
-import org.bouncycastle.crypto.digests.SHA224Digest;
-import org.bouncycastle.crypto.digests.SHA256Digest;
-import org.bouncycastle.crypto.digests.SHA384Digest;
-import org.bouncycastle.crypto.digests.SHA3Digest;
-import org.bouncycastle.crypto.digests.SHA512Digest;
+import org.bouncycastle.crypto.digests.*;
 import org.bouncycastle.operator.OperatorCreationException;
 
 public class BcDefaultDigestProvider
@@ -126,6 +115,20 @@
                 return new GOST3411Digest();
             }
         });
+        table.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_256, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new GOST3411_2012_256Digest();
+            }
+        });
+        table.put(RosstandartObjectIdentifiers.id_tc26_gost_3411_12_512, new BcDigestProvider()
+        {
+            public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
+            {
+                return new GOST3411_2012_512Digest();
+            }
+        });
         table.put(TeleTrusTObjectIdentifiers.ripemd128, new BcDigestProvider()
         {
             public ExtendedDigest get(AlgorithmIdentifier digestAlgorithmIdentifier)
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
index 5583194..b834458 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentSignerBuilder.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.operator.jcajce;
 
-import java.io.IOException;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 import java.security.PrivateKey;
@@ -8,15 +7,23 @@
 import java.security.SecureRandom;
 import java.security.Signature;
 import java.security.SignatureException;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.spec.PSSParameterSpec;
 
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.jcajce.io.OutputStreamFactory;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.ContentSigner;
+import org.bouncycastle.operator.DefaultDigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.DefaultSignatureAlgorithmIdentifierFinder;
+import org.bouncycastle.operator.DigestAlgorithmIdentifierFinder;
 import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.OperatorStreamException;
 import org.bouncycastle.operator.RuntimeOperatorException;
 
 public class JcaContentSignerBuilder
@@ -25,11 +32,32 @@
     private SecureRandom random;
     private String signatureAlgorithm;
     private AlgorithmIdentifier sigAlgId;
+    private AlgorithmParameterSpec sigAlgSpec;
 
     public JcaContentSignerBuilder(String signatureAlgorithm)
     {
         this.signatureAlgorithm = signatureAlgorithm;
         this.sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find(signatureAlgorithm);
+        this.sigAlgSpec = null;
+    }
+
+    public JcaContentSignerBuilder(String signatureAlgorithm, AlgorithmParameterSpec sigParamSpec)
+    {
+        this.signatureAlgorithm = signatureAlgorithm;
+
+        if (sigParamSpec instanceof PSSParameterSpec)
+        {
+            PSSParameterSpec pssSpec = (PSSParameterSpec)sigParamSpec;
+
+            this.sigAlgSpec = pssSpec;
+            this.sigAlgId = new AlgorithmIdentifier(
+                                    PKCSObjectIdentifiers.id_RSASSA_PSS, createPSSParams(pssSpec));
+        }
+        else
+        {
+            throw new IllegalArgumentException("unknown sigParamSpec: "
+                            + ((sigParamSpec == null) ? "null" : sigParamSpec.getClass().getName()));
+        }
     }
 
     public JcaContentSignerBuilder setProvider(Provider provider)
@@ -72,7 +100,7 @@
 
             return new ContentSigner()
             {
-                private SignatureOutputStream stream = new SignatureOutputStream(sig);
+                private OutputStream stream = OutputStreamFactory.createStream(sig);
 
                 public AlgorithmIdentifier getAlgorithmIdentifier()
                 {
@@ -88,7 +116,7 @@
                 {
                     try
                     {
-                        return stream.getSignature();
+                        return sig.sign();
                     }
                     catch (SignatureException e)
                     {
@@ -103,59 +131,16 @@
         }
     }
 
-    private class SignatureOutputStream
-        extends OutputStream
+    private static RSASSAPSSparams createPSSParams(PSSParameterSpec pssSpec)
     {
-        private Signature sig;
+        DigestAlgorithmIdentifierFinder digFinder = new DefaultDigestAlgorithmIdentifierFinder();
+           AlgorithmIdentifier digId = digFinder.find(pssSpec.getDigestAlgorithm());
+           AlgorithmIdentifier mgfDig = digFinder.find(((MGF1ParameterSpec)pssSpec.getMGFParameters()).getDigestAlgorithm());
 
-        SignatureOutputStream(Signature sig)
-        {
-            this.sig = sig;
-        }
-
-        public void write(byte[] bytes, int off, int len)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes, off, len);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(byte[] bytes)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(int b)
-            throws IOException
-        {
-            try
-            {
-                sig.update((byte)b);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        byte[] getSignature()
-            throws SignatureException
-        {
-            return sig.sign();
-        }
+        return new RSASSAPSSparams(
+            digId,
+            new AlgorithmIdentifier(PKCSObjectIdentifiers.id_mgf1, mgfDig),
+            new ASN1Integer(pssSpec.getSaltLength()),
+            new ASN1Integer(pssSpec.getTrailerField()));
     }
 }
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 5f82d40..67150bc 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JcaContentVerifierProviderBuilder.java
@@ -1,6 +1,5 @@
 package org.bouncycastle.operator.jcajce;
 
-import java.io.IOException;
 import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 import java.security.Provider;
@@ -15,13 +14,13 @@
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.cert.X509CertificateHolder;
 import org.bouncycastle.cert.jcajce.JcaX509CertificateHolder;
+import org.bouncycastle.jcajce.io.OutputStreamFactory;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.ContentVerifier;
 import org.bouncycastle.operator.ContentVerifierProvider;
 import org.bouncycastle.operator.OperatorCreationException;
-import org.bouncycastle.operator.OperatorStreamException;
 import org.bouncycastle.operator.RawContentVerifier;
 import org.bouncycastle.operator.RuntimeOperatorException;
 
@@ -69,8 +68,6 @@
 
         return new ContentVerifierProvider()
         {
-            private SignatureOutputStream stream;
-
             public boolean hasAssociatedCertificate()
             {
                 return true;
@@ -84,13 +81,12 @@
             public ContentVerifier get(AlgorithmIdentifier algorithm)
                 throws OperatorCreationException
             {
+                Signature sig;
                 try
                 {
-                    Signature sig = helper.createSignature(algorithm);
+                    sig = helper.createSignature(algorithm);
 
                     sig.initVerify(certificate.getPublicKey());
-
-                    stream = new SignatureOutputStream(sig);
                 }
                 catch (GeneralSecurityException e)
                 {
@@ -101,11 +97,11 @@
 
                 if (rawSig != null)
                 {
-                    return new RawSigVerifier(algorithm, stream, rawSig);
+                    return new RawSigVerifier(algorithm, sig, rawSig);
                 }
                 else
                 {
-                    return new SigVerifier(algorithm, stream);
+                    return new SigVerifier(algorithm, sig);
                 }
             }
         };
@@ -129,17 +125,17 @@
             public ContentVerifier get(AlgorithmIdentifier algorithm)
                 throws OperatorCreationException
             {
-                SignatureOutputStream stream = createSignatureStream(algorithm, publicKey);
+                Signature sig = createSignature(algorithm, publicKey);
 
                 Signature rawSig = createRawSig(algorithm, publicKey);
 
                 if (rawSig != null)
                 {
-                    return new RawSigVerifier(algorithm, stream, rawSig);
+                    return new RawSigVerifier(algorithm, sig, rawSig);
                 }
                 else
                 {
-                    return new SigVerifier(algorithm, stream);
+                    return new SigVerifier(algorithm, sig);
                 }
             }
         };
@@ -151,7 +147,7 @@
         return this.build(helper.convertPublicKey(publicKey));
     }
 
-    private SignatureOutputStream createSignatureStream(AlgorithmIdentifier algorithm, PublicKey publicKey)
+    private Signature createSignature(AlgorithmIdentifier algorithm, PublicKey publicKey)
         throws OperatorCreationException
     {
         try
@@ -160,7 +156,7 @@
 
             sig.initVerify(publicKey);
 
-            return new SignatureOutputStream(sig);
+            return sig;
         }
         catch (GeneralSecurityException e)
         {
@@ -190,14 +186,16 @@
     private class SigVerifier
         implements ContentVerifier
     {
-        private AlgorithmIdentifier algorithm;
+        private final AlgorithmIdentifier algorithm;
+        private final Signature signature;
 
-        protected SignatureOutputStream stream;
+        protected final OutputStream stream;
 
-        SigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream)
+        SigVerifier(AlgorithmIdentifier algorithm, Signature signature)
         {
             this.algorithm = algorithm;
-            this.stream = stream;
+            this.signature = signature;
+            this.stream = OutputStreamFactory.createStream(signature);
         }
 
         public AlgorithmIdentifier getAlgorithmIdentifier()
@@ -219,7 +217,7 @@
         {
             try
             {
-                return stream.verify(expected);
+                return signature.verify(expected);
             }
             catch (SignatureException e)
             {
@@ -234,9 +232,9 @@
     {
         private Signature rawSignature;
 
-        RawSigVerifier(AlgorithmIdentifier algorithm, SignatureOutputStream stream, Signature rawSignature)
+        RawSigVerifier(AlgorithmIdentifier algorithm, Signature standardSig, Signature rawSignature)
         {
-            super(algorithm, stream);
+            super(algorithm, standardSig);
             this.rawSignature = rawSignature;
         }
 
@@ -279,7 +277,7 @@
                 // standard signature will not be freed if verify is not called on it.
                 try
                 {
-                    stream.verify(expected);
+                    rawSignature.verify(expected);
                 }
                 catch (Exception e)
                 {
@@ -288,60 +286,4 @@
             }
         }
     }
-
-    private class SignatureOutputStream
-        extends OutputStream
-    {
-        private Signature sig;
-
-        SignatureOutputStream(Signature sig)
-        {
-            this.sig = sig;
-        }
-
-        public void write(byte[] bytes, int off, int len)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes, off, len);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(byte[] bytes)
-            throws IOException
-        {
-            try
-            {
-                sig.update(bytes);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        public void write(int b)
-            throws IOException
-        {
-            try
-            {
-                sig.update((byte)b);
-            }
-            catch (SignatureException e)
-            {
-                throw new OperatorStreamException("exception in content signer: " + e.getMessage(), e);
-            }
-        }
-
-        boolean verify(byte[] expected)
-            throws SignatureException
-        {
-            return sig.verify(expected);
-        }
-    }
 }
\ No newline at end of file
diff --git a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
index 2bbdd5f..bbde317 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyUnwrapper.java
@@ -77,7 +77,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return  the current Unwrapper.
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 b8063b4..2f14c1f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceAsymmetricKeyWrapper.java
@@ -3,39 +3,72 @@
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.InvalidKeyException;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
 import java.security.Provider;
 import java.security.ProviderException;
 import java.security.PublicKey;
 import java.security.SecureRandom;
 import java.security.cert.X509Certificate;
+import java.security.interfaces.ECPublicKey;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.MGF1ParameterSpec;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.Map;
+import java.util.Set;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
+import javax.crypto.SecretKey;
 import javax.crypto.spec.OAEPParameterSpec;
 import javax.crypto.spec.PSource;
+import javax.crypto.spec.SecretKeySpec;
 
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.DERNull;
 import org.bouncycastle.asn1.DEROctetString;
+import org.bouncycastle.asn1.cryptopro.CryptoProObjectIdentifiers;
+import org.bouncycastle.asn1.cryptopro.Gost2814789EncryptedKey;
+import org.bouncycastle.asn1.cryptopro.GostR3410KeyTransport;
+import org.bouncycastle.asn1.cryptopro.GostR3410TransportParameters;
 import org.bouncycastle.asn1.nist.NISTObjectIdentifiers;
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSAESOAEPparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
+import org.bouncycastle.jcajce.spec.GOST28147WrapParameterSpec;
+import org.bouncycastle.jcajce.spec.UserKeyingMaterialSpec;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
 import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
 import org.bouncycastle.operator.AsymmetricKeyWrapper;
 import org.bouncycastle.operator.GenericKey;
 import org.bouncycastle.operator.OperatorException;
+import org.bouncycastle.util.Arrays;
 
 public class JceAsymmetricKeyWrapper
     extends AsymmetricKeyWrapper
 {
+    private static final Set gostAlgs = new HashSet();
+
+    static
+    {
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001_CryptoPro_ESDH);
+        gostAlgs.add(CryptoProObjectIdentifiers.gostR3410_2001);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_agreement_gost_3410_12_512);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_256);
+        gostAlgs.add(RosstandartObjectIdentifiers.id_tc26_gost_3410_12_512);
+    }
+
+    static boolean isGOST(ASN1ObjectIdentifier algorithm)
+    {
+        return gostAlgs.contains(algorithm);
+    }
+
     private OperatorHelper helper = new OperatorHelper(new DefaultJcaJceHelper());
     private Map extraMappings = new HashMap();
     private PublicKey publicKey;
@@ -110,7 +143,6 @@
      * <pre>
      *     unwrapper.setAlgorithmMapping(PKCSObjectIdentifiers.rsaEncryption, "RSA");
      * </pre>
-     * </p>
      * @param algorithm  OID of algorithm in recipient.
      * @param algorithmName JCE algorithm name to use.
      * @return the current Wrapper.
@@ -125,54 +157,117 @@
     public byte[] generateWrappedKey(GenericKey encryptionKey)
         throws OperatorException
     {
-        Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), extraMappings);
-        AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier());
-
         byte[] encryptedKeyBytes = null;
 
-        try
-        {
-            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)
-        {
-        }
-        catch (IllegalStateException e)
-        {
-        }
-        catch (UnsupportedOperationException e)
-        {
-        }
-        catch (ProviderException e)
-        {
-        }
-
-        // some providers do not support WRAP (this appears to be only for asymmetric algorithms)
-        if (encryptedKeyBytes == null)
+        if (isGOST(getAlgorithmIdentifier().getAlgorithm()))
         {
             try
             {
-                keyEncryptionCipher.init(Cipher.ENCRYPT_MODE, publicKey, random);
-                encryptedKeyBytes = keyEncryptionCipher.doFinal(OperatorUtils.getJceKey(encryptionKey).getEncoded());
+                if (random == null)
+                {
+                    random = new SecureRandom();
+                }
+                KeyPairGenerator kpGen = helper.createKeyPairGenerator(getAlgorithmIdentifier().getAlgorithm());
+
+                kpGen.initialize(((ECPublicKey)publicKey).getParams(), random);
+
+                KeyPair ephKp = kpGen.generateKeyPair();
+
+                byte[] ukm = new byte[8];
+
+                random.nextBytes(ukm);
+
+                SubjectPublicKeyInfo ephKeyInfo = SubjectPublicKeyInfo.getInstance(ephKp.getPublic().getEncoded());
+
+                GostR3410TransportParameters transParams;
+
+                if (ephKeyInfo.getAlgorithm().getAlgorithm().on(RosstandartObjectIdentifiers.id_tc26))
+                {
+                    transParams = new GostR3410TransportParameters(
+                        RosstandartObjectIdentifiers.id_tc26_gost_28147_param_Z, ephKeyInfo, ukm);
+                }
+                else
+                {
+                    transParams = new GostR3410TransportParameters(
+                                CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_A_ParamSet, ephKeyInfo, ukm);
+                }
+
+                KeyAgreement agreement = helper.createKeyAgreement(getAlgorithmIdentifier().getAlgorithm());
+
+                agreement.init(ephKp.getPrivate(), new UserKeyingMaterialSpec(transParams.getUkm()));
+
+                agreement.doPhase(publicKey, true);
+
+                SecretKey key = agreement.generateSecret(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap.getId());
+      
+                byte[] encKey = OperatorUtils.getJceKey(encryptionKey).getEncoded();
+
+                Cipher keyCipher = helper.createCipher(CryptoProObjectIdentifiers.id_Gost28147_89_CryptoPro_KeyWrap);
+
+                keyCipher.init(Cipher.WRAP_MODE, key, new GOST28147WrapParameterSpec(transParams.getEncryptionParamSet(), transParams.getUkm()));
+
+                byte[] keyData = keyCipher.wrap(new SecretKeySpec(encKey, "GOST"));
+
+                GostR3410KeyTransport transport = new GostR3410KeyTransport(
+                                new Gost2814789EncryptedKey(
+                                    Arrays.copyOfRange(keyData, 0, 32), Arrays.copyOfRange(keyData, 32, 36)), transParams);
+
+                return transport.getEncoded();
+            }
+            catch (Exception e)
+            {
+                throw new OperatorException("exception wrapping key: " + e.getMessage(), e);
+            }
+        }
+        else
+        {
+            Cipher keyEncryptionCipher = helper.createAsymmetricWrapper(getAlgorithmIdentifier().getAlgorithm(), extraMappings);
+            AlgorithmParameters algParams = helper.createAlgorithmParameters(this.getAlgorithmIdentifier());
+
+            try
+            {
+                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)
             {
-                throw new OperatorException("unable to encrypt contents key", e);
             }
             catch (GeneralSecurityException e)
             {
-                throw new OperatorException("unable to encrypt contents key", e);
+            }
+            catch (IllegalStateException e)
+            {
+            }
+            catch (UnsupportedOperationException e)
+            {
+            }
+            catch (ProviderException e)
+            {
+            }
+
+            // some providers do not support WRAP (this appears to be only for asymmetric algorithms)
+            if (encryptedKeyBytes == null)
+            {
+                try
+                {
+                    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/JceInputDecryptorProviderBuilder.java b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceInputDecryptorProviderBuilder.java
new file mode 100644
index 0000000..e615b88
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/JceInputDecryptorProviderBuilder.java
@@ -0,0 +1,113 @@
+package org.bouncycastle.operator.jcajce;
+
+import java.io.InputStream;
+import java.security.Provider;
+
+import javax.crypto.Cipher;
+import javax.crypto.CipherInputStream;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+
+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.x509.AlgorithmIdentifier;
+import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
+import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
+import org.bouncycastle.jcajce.util.JcaJceHelper;
+import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
+import org.bouncycastle.jcajce.util.ProviderJcaJceHelper;
+import org.bouncycastle.operator.InputDecryptor;
+import org.bouncycastle.operator.InputDecryptorProvider;
+import org.bouncycastle.operator.OperatorCreationException;
+import org.bouncycastle.util.Arrays;
+
+/**
+ * A generic decryptor provider for IETF style algorithms.
+ */
+public class JceInputDecryptorProviderBuilder
+{
+    private JcaJceHelper helper = new DefaultJcaJceHelper();
+
+    public JceInputDecryptorProviderBuilder()
+    {
+    }
+
+    public JceInputDecryptorProviderBuilder setProvider(Provider provider)
+    {
+        this.helper = new ProviderJcaJceHelper(provider);
+
+        return this;
+    }
+
+    public JceInputDecryptorProviderBuilder setProvider(String providerName)
+    {
+        this.helper = new NamedJcaJceHelper(providerName);
+
+        return this;
+    }
+
+    /**
+     * Build a decryptor provider which will use the passed in bytes for the symmetric key.
+     *
+     * @param keyBytes bytes representing the key to use.
+     * @return an decryptor provider.
+     */
+    public InputDecryptorProvider build(byte[] keyBytes)
+    {
+        final byte[] encKeyBytes = Arrays.clone(keyBytes);
+
+        return new InputDecryptorProvider()
+        {
+            private Cipher cipher;
+            private AlgorithmIdentifier encryptionAlg;
+
+            public InputDecryptor get(final AlgorithmIdentifier algorithmIdentifier)
+                throws OperatorCreationException
+            {
+                encryptionAlg = algorithmIdentifier;
+
+                ASN1ObjectIdentifier algorithm = algorithmIdentifier.getAlgorithm();
+
+                try
+                {
+                    cipher = helper.createCipher(algorithm.getId());
+                    SecretKey key = new SecretKeySpec(encKeyBytes, algorithm.getId());
+                    
+                    ASN1Encodable encParams = algorithmIdentifier.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)
+                {
+                    throw new OperatorCreationException("unable to create InputDecryptor: " + e.getMessage(), e);
+                }
+
+                return new InputDecryptor()
+                {
+                    public AlgorithmIdentifier getAlgorithmIdentifier()
+                    {
+                        return encryptionAlg;
+                    }
+
+                    public InputStream getInputStream(InputStream input)
+                    {
+                        return new CipherInputStream(input, cipher);
+                    }
+                };
+            }
+        };
+    }
+}
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 9431ad3..23e4d3b 100644
--- a/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
+++ b/bcpkix/src/main/java/org/bouncycastle/operator/jcajce/OperatorHelper.java
@@ -5,6 +5,7 @@
 import java.security.AlgorithmParameters;
 import java.security.GeneralSecurityException;
 import java.security.KeyFactory;
+import java.security.KeyPairGenerator;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
@@ -20,6 +21,7 @@
 import java.util.Map;
 
 import javax.crypto.Cipher;
+import javax.crypto.KeyAgreement;
 
 import org.bouncycastle.asn1.ASN1Encodable;
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
@@ -34,11 +36,13 @@
 import org.bouncycastle.asn1.oiw.OIWObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.pkcs.RSASSAPSSparams;
+import org.bouncycastle.asn1.rosstandart.RosstandartObjectIdentifiers;
 import org.bouncycastle.asn1.teletrust.TeleTrusTObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
 import org.bouncycastle.asn1.x9.X9ObjectIdentifiers;
 import org.bouncycastle.cert.X509CertificateHolder;
+import org.bouncycastle.cms.CMSException;
 import org.bouncycastle.jcajce.util.AlgorithmParametersUtils;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.MessageDigestUtils;
@@ -65,6 +69,8 @@
         oids.put(PKCSObjectIdentifiers.sha512WithRSAEncryption, "SHA512WITHRSA");
         oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_94, "GOST3411WITHGOST3410");
         oids.put(CryptoProObjectIdentifiers.gostR3411_94_with_gostR3410_2001, "GOST3411WITHECGOST3410");
+        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_256, "GOST3411-2012-256WITHECGOST3410-2012-256");
+        oids.put(RosstandartObjectIdentifiers.id_tc26_signwithdigest_gost_3410_12_512, "GOST3411-2012-512WITHECGOST3410-2012-512");
         oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA1, "SHA1WITHPLAIN-ECDSA");
         oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA224, "SHA224WITHPLAIN-ECDSA");
         oids.put(BSIObjectIdentifiers.ecdsa_plain_SHA256, "SHA256WITHPLAIN-ECDSA");
@@ -90,17 +96,19 @@
         oids.put(NISTObjectIdentifiers.dsa_with_sha224, "SHA224WITHDSA");
         oids.put(NISTObjectIdentifiers.dsa_with_sha256, "SHA256WITHDSA");
 
-        oids.put(OIWObjectIdentifiers.idSHA1, "SHA-1");
-        oids.put(NISTObjectIdentifiers.id_sha224, "SHA-224");
-        oids.put(NISTObjectIdentifiers.id_sha256, "SHA-256");
-        oids.put(NISTObjectIdentifiers.id_sha384, "SHA-384");
-        oids.put(NISTObjectIdentifiers.id_sha512, "SHA-512");
+        oids.put(OIWObjectIdentifiers.idSHA1, "SHA1");
+        oids.put(NISTObjectIdentifiers.id_sha224, "SHA224");
+        oids.put(NISTObjectIdentifiers.id_sha256, "SHA256");
+        oids.put(NISTObjectIdentifiers.id_sha384, "SHA384");
+        oids.put(NISTObjectIdentifiers.id_sha512, "SHA512");
         oids.put(TeleTrusTObjectIdentifiers.ripemd128, "RIPEMD128");
         oids.put(TeleTrusTObjectIdentifiers.ripemd160, "RIPEMD160");
         oids.put(TeleTrusTObjectIdentifiers.ripemd256, "RIPEMD256");
 
         asymmetricWrapperAlgNames.put(PKCSObjectIdentifiers.rsaEncryption, "RSA/ECB/PKCS1Padding");
 
+        asymmetricWrapperAlgNames.put(CryptoProObjectIdentifiers.gostR3410_2001, "ECGOST3410");
+
         symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMS3DESwrap, "DESEDEWrap");
         symmetricWrapperAlgNames.put(PKCSObjectIdentifiers.id_alg_CMSRC2wrap, "RC2Wrap");
         symmetricWrapperAlgNames.put(NISTObjectIdentifiers.id_aes128_wrap, "AESWrap");
@@ -147,6 +155,73 @@
         return ((Integer)symmetricWrapperKeySizes.get(algOid)).intValue();
     }
 
+    KeyPairGenerator createKeyPairGenerator(ASN1ObjectIdentifier algorithm)
+        throws CMSException
+    {
+        try
+        {
+            String agreementName = null; //(String)BASE_CIPHER_NAMES.get(algorithm);
+
+            if (agreementName != null)
+            {
+                try
+                {
+                    // this is reversed as the Sun policy files now allow unlimited strength RSA
+                    return helper.createKeyPairGenerator(agreementName);
+                }
+                catch (NoSuchAlgorithmException e)
+                {
+                    // Ignore
+                }
+            }
+            return helper.createKeyPairGenerator(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new CMSException("cannot create key agreement: " + e.getMessage(), e);
+        }
+    }
+
+    Cipher createCipher(ASN1ObjectIdentifier algorithm)
+        throws OperatorCreationException
+    {
+        try
+        {
+            return helper.createCipher(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create cipher: " + e.getMessage(), e);
+        }
+    }
+
+    KeyAgreement createKeyAgreement(ASN1ObjectIdentifier algorithm)
+        throws OperatorCreationException
+    {
+        try
+        {
+            String agreementName = null; //(String)BASE_CIPHER_NAMES.get(algorithm);
+
+            if (agreementName != null)
+            {
+                try
+                {
+                    // this is reversed as the Sun policy files now allow unlimited strength RSA
+                    return helper.createKeyAgreement(agreementName);
+                }
+                catch (NoSuchAlgorithmException e)
+                {
+                    // Ignore
+                }
+            }
+            return helper.createKeyAgreement(algorithm.getId());
+        }
+        catch (GeneralSecurityException e)
+        {
+            throw new OperatorCreationException("cannot create key agreement: " + e.getMessage(), e);
+        }
+    }
+
     Cipher createAsymmetricWrapper(ASN1ObjectIdentifier algorithm, Map extraAlgNames)
         throws OperatorCreationException
     {
@@ -275,7 +350,7 @@
             //
             if (oids.get(digAlgId.getAlgorithm()) != null)
             {
-                String  digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
+                String digestAlgorithm = (String)oids.get(digAlgId.getAlgorithm());
 
                 dig = helper.createDigest(digestAlgorithm);
             }
@@ -291,7 +366,7 @@
     Signature createSignature(AlgorithmIdentifier sigAlgId)
         throws GeneralSecurityException
     {
-        Signature   sig;
+        Signature sig;
 
         try
         {
@@ -304,7 +379,7 @@
             //
             if (oids.get(sigAlgId.getAlgorithm()) != null)
             {
-                String  signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
+                String signatureAlgorithm = (String)oids.get(sigAlgId.getAlgorithm());
 
                 sig = helper.createSignature(signatureAlgorithm);
             }
@@ -317,7 +392,7 @@
         if (sigAlgId.getAlgorithm().equals(PKCSObjectIdentifiers.id_RSASSA_PSS))
         {
             ASN1Sequence seq = ASN1Sequence.getInstance(sigAlgId.getParameters());
-          
+
             if (notDefaultPSSParams(seq))
             {
                 try
@@ -340,7 +415,7 @@
 
     public Signature createRawSignature(AlgorithmIdentifier algorithm)
     {
-        Signature   sig;
+        Signature sig;
 
         try
         {
@@ -404,7 +479,7 @@
             return name.substring(0, dIndex) + name.substring(dIndex + 1);
         }
 
-        return MessageDigestUtils.getDigestName(oid);
+        return name;
     }
 
     public X509Certificate convertCertificate(X509CertificateHolder certHolder)
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java
index c4785f6..dd48195 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS10CertificationRequestBuilder.java
@@ -38,8 +38,8 @@
  *  Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
  *
  *  Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
- *    type    ATTRIBUTE.&id({IOSet}),
- *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{\@type})
+ *    type    ATTRIBUTE.&amp;id({IOSet}),
+ *    values  SET SIZE(1..MAX) OF ATTRIBUTE.&amp;Type({IOSet}{\@type})
  *  }
  * </pre>
  */
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
index a1c9164..324e528 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfo.java
@@ -6,6 +6,7 @@
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.pkcs.EncryptedPrivateKeyInfo;
 import org.bouncycastle.asn1.pkcs.PrivateKeyInfo;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
 import org.bouncycastle.operator.InputDecryptor;
 import org.bouncycastle.operator.InputDecryptorProvider;
 import org.bouncycastle.util.io.Streams;
@@ -45,6 +46,16 @@
         this(parseBytes(encryptedPrivateKeyInfo));
     }
 
+    public AlgorithmIdentifier getEncryptionAlgorithm()
+    {
+        return encryptedPrivateKeyInfo.getEncryptionAlgorithm();
+    }
+
+    public byte[] getEncryptedData()
+    {
+        return encryptedPrivateKeyInfo.getEncryptedData();
+    }
+
     public EncryptedPrivateKeyInfo toASN1Structure()
     {
          return encryptedPrivateKeyInfo;
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
index 653aa57..9bafaf6 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCS8EncryptedPrivateKeyInfoBuilder.java
@@ -27,6 +27,11 @@
 {
     private PrivateKeyInfo privateKeyInfo;
 
+    public PKCS8EncryptedPrivateKeyInfoBuilder(byte[] privateKeyInfo)
+    {
+        this(PrivateKeyInfo.getInstance(privateKeyInfo));
+    }
+
     public PKCS8EncryptedPrivateKeyInfoBuilder(PrivateKeyInfo privateKeyInfo)
     {
         this.privateKeyInfo = privateKeyInfo;
diff --git a/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSUtils.java b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSUtils.java
new file mode 100644
index 0000000..61e6f94
--- /dev/null
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/PKCSUtils.java
@@ -0,0 +1,39 @@
+package org.bouncycastle.pkcs;
+
+import java.util.HashMap;
+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.pkcs.PKCSObjectIdentifiers;
+import org.bouncycastle.util.Integers;
+
+class PKCSUtils
+{
+    private static final Map PRFS_SALT = new HashMap();
+
+    static
+    {
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA1, Integers.valueOf(20));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA256, Integers.valueOf(32));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA512, Integers.valueOf(64));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA224, Integers.valueOf(28));
+        PRFS_SALT.put(PKCSObjectIdentifiers.id_hmacWithSHA384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_224, Integers.valueOf(28));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_256, Integers.valueOf(32));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_384, Integers.valueOf(48));
+        PRFS_SALT.put(NISTObjectIdentifiers.id_hmacWithSHA3_512, Integers.valueOf(64));
+        PRFS_SALT.put(CryptoProObjectIdentifiers.gostR3411Hmac, Integers.valueOf(32));
+    }
+
+    static int getSaltSize(ASN1ObjectIdentifier algorithm)
+    {
+        if (!PRFS_SALT.containsKey(algorithm))
+        {
+            throw new IllegalStateException("no salt size for algorithm: " + algorithm);
+        }
+
+        return ((Integer)PRFS_SALT.get(algorithm)).intValue();
+    }
+}
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 d7627ea..330332d 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEInputDecryptorProviderBuilder.java
@@ -15,14 +15,20 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1OctetString;
 import org.bouncycastle.asn1.cryptopro.GOST28147Parameters;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.ScryptParams;
+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.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.PasswordConverter;
+import org.bouncycastle.jcajce.PBKDF1Key;
 import org.bouncycastle.jcajce.PKCS12KeyWithParameters;
 import org.bouncycastle.jcajce.spec.GOST28147ParameterSpec;
 import org.bouncycastle.jcajce.spec.PBKDF2KeySpec;
+import org.bouncycastle.jcajce.spec.ScryptKeySpec;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -107,18 +113,32 @@
                     else if (algorithm.equals(PKCSObjectIdentifiers.id_PBES2))
                     {
                         PBES2Parameters alg = PBES2Parameters.getInstance(algorithmIdentifier.getParameters());
-                        PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
-                        AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
 
-                        SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId());
-
-                        if (func.isDefaultPrf())
+                        if (MiscObjectIdentifiers.id_scrypt.equals(alg.getKeyDerivationFunc().getAlgorithm()))
                         {
-                            key = keyFact.generateSecret(new PBEKeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme)));
+                            ScryptParams params = ScryptParams.getInstance(alg.getKeyDerivationFunc().getParameters());
+                            AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+                            SecretKeyFactory keyFact = helper.createSecretKeyFactory("SCRYPT");
+
+                            key = keyFact.generateSecret(new ScryptKeySpec(password,
+                                       params.getSalt(), params.getCostParameter().intValue(), params.getBlockSize().intValue(),
+                                       params.getParallelizationParameter().intValue(), keySizeProvider.getKeySize(encScheme)));
                         }
                         else
                         {
-                            key = keyFact.generateSecret(new PBKDF2KeySpec(password, func.getSalt(), func.getIterationCount().intValue(), keySizeProvider.getKeySize(encScheme), func.getPrf()));
+                            SecretKeyFactory keyFact = helper.createSecretKeyFactory(alg.getKeyDerivationFunc().getAlgorithm().getId());
+                            PBKDF2Params func = PBKDF2Params.getInstance(alg.getKeyDerivationFunc().getParameters());
+                            AlgorithmIdentifier encScheme = AlgorithmIdentifier.getInstance(alg.getEncryptionScheme());
+
+                            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());
@@ -138,6 +158,20 @@
                             cipher.init(Cipher.DECRYPT_MODE, key, new GOST28147ParameterSpec(gParams.getEncryptionParamSet(), gParams.getIV()));
                         }
                     }
+                    else if (algorithm.equals(PKCSObjectIdentifiers.pbeWithMD5AndDES_CBC)
+                        || algorithm.equals(PKCSObjectIdentifiers.pbeWithSHA1AndDES_CBC))
+                    {
+                        PBEParameter pbeParams = PBEParameter.getInstance(algorithmIdentifier.getParameters());
+
+                        cipher = helper.createCipher(algorithm.getId());
+
+                        cipher.init(Cipher.DECRYPT_MODE, new PBKDF1Key(password, PasswordConverter.ASCII),
+                                new PBEParameterSpec(pbeParams.getSalt(), pbeParams.getIterationCount().intValue()));
+                    }
+                    else
+                    {
+                        throw new OperatorCreationException("unable to create InputDecryptor: algorithm " + algorithm + " unknown.");
+                    }
                 }
                 catch (Exception e)
                 {
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 06d60d2..8763b3f 100644
--- a/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
+++ b/bcpkix/src/main/java/org/bouncycastle/pkcs/jcajce/JcePKCSPBEOutputEncryptorBuilder.java
@@ -13,6 +13,8 @@
 import org.bouncycastle.asn1.ASN1ObjectIdentifier;
 import org.bouncycastle.asn1.ASN1Primitive;
 import org.bouncycastle.asn1.bc.BCObjectIdentifiers;
+import org.bouncycastle.asn1.misc.MiscObjectIdentifiers;
+import org.bouncycastle.asn1.misc.ScryptParams;
 import org.bouncycastle.asn1.pkcs.EncryptionScheme;
 import org.bouncycastle.asn1.pkcs.KeyDerivationFunc;
 import org.bouncycastle.asn1.pkcs.PBES2Parameters;
@@ -20,7 +22,11 @@
 import org.bouncycastle.asn1.pkcs.PKCS12PBEParams;
 import org.bouncycastle.asn1.pkcs.PKCSObjectIdentifiers;
 import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.util.PBKDF2Config;
+import org.bouncycastle.crypto.util.PBKDFConfig;
+import org.bouncycastle.crypto.util.ScryptConfig;
 import org.bouncycastle.jcajce.PKCS12KeyWithParameters;
+import org.bouncycastle.jcajce.spec.ScryptKeySpec;
 import org.bouncycastle.jcajce.util.DefaultJcaJceHelper;
 import org.bouncycastle.jcajce.util.JcaJceHelper;
 import org.bouncycastle.jcajce.util.NamedJcaJceHelper;
@@ -33,27 +39,44 @@
 
 public class JcePKCSPBEOutputEncryptorBuilder
 {
+    private final PBKDFConfig pbkdf;
+
     private JcaJceHelper helper = new DefaultJcaJceHelper();
     private ASN1ObjectIdentifier algorithm;
     private ASN1ObjectIdentifier keyEncAlgorithm;
     private SecureRandom random;
     private SecretKeySizeProvider keySizeProvider = DefaultSecretKeySizeProvider.INSTANCE;
     private int iterationCount = 1024;
+    private PBKDF2Config.Builder pbkdfBuilder = new PBKDF2Config.Builder();
 
-    public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier algorithm)
+    public JcePKCSPBEOutputEncryptorBuilder(ASN1ObjectIdentifier keyEncryptionAlg)
     {
-        if (isPKCS12(algorithm))
+        this.pbkdf = null;
+        if (isPKCS12(keyEncryptionAlg))
         {
-            this.algorithm = algorithm;
-            this.keyEncAlgorithm = algorithm;
+            this.algorithm = keyEncryptionAlg;
+            this.keyEncAlgorithm = keyEncryptionAlg;